Commit 800a85f7bc6a327109a54d62993b7e0be97eb2eb

Authored by YunaiV
1 parent 479e0356

reactor:【IoT 物联网】清理 components 和 protocol,基本已经融合到 gateway

Showing 63 changed files with 1 additions and 5759 deletions
yudao-module-iot/pom.xml
... ... @@ -9,8 +9,6 @@
9 9 <modules>
10 10 <module>yudao-module-iot-api</module>
11 11 <module>yudao-module-iot-biz</module>
12   - <module>yudao-module-iot-net-components</module>
13   - <module>yudao-module-iot-protocol</module>
14 12 <module>yudao-module-iot-core</module>
15 13 <module>yudao-module-iot-gateway</module>
16 14 </modules>
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceConfigSetReqDTO.java deleted
1   -package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream;
2   -
3   -import jakarta.validation.constraints.NotNull;
4   -import lombok.Data;
5   -
6   -import java.util.Map;
7   -
8   -/**
9   - * IoT 设备【配置】设置 Request DTO
10   - *
11   - * @author 芋道源码
12   - */
13   -@Data
14   -public class IotDeviceConfigSetReqDTO extends IotDeviceDownstreamAbstractReqDTO {
15   -
16   - /**
17   - * 配置
18   - */
19   - @NotNull(message = "配置不能为空")
20   - private Map<String, Object> config;
21   -
22   -}
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceDownstreamAbstractReqDTO.java deleted
1   -package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream;
2   -
3   -import jakarta.validation.constraints.NotEmpty;
4   -import lombok.Data;
5   -
6   -/**
7   - * IoT 设备下行的抽象 Request DTO
8   - *
9   - * @author 芋道源码
10   - */
11   -@Data
12   -public abstract class IotDeviceDownstreamAbstractReqDTO {
13   -
14   - /**
15   - * 请求编号
16   - */
17   - private String requestId;
18   -
19   - /**
20   - * 产品标识
21   - */
22   - @NotEmpty(message = "产品标识不能为空")
23   - private String productKey;
24   - /**
25   - * 设备名称
26   - */
27   - @NotEmpty(message = "设备名称不能为空")
28   - private String deviceName;
29   -
30   -}
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceOtaUpgradeReqDTO.java deleted
1   -package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream;
2   -
3   -import cn.hutool.core.map.MapUtil;
4   -import lombok.Data;
5   -
6   -import java.util.Map;
7   -
8   -/**
9   - * IoT 设备【OTA】升级下发 Request DTO(更新固件消息)
10   - *
11   - * @author 芋道源码
12   - */
13   -@Data
14   -public class IotDeviceOtaUpgradeReqDTO extends IotDeviceDownstreamAbstractReqDTO {
15   -
16   - /**
17   - * 固件编号
18   - */
19   - private Long firmwareId;
20   - /**
21   - * 固件版本
22   - */
23   - private String version;
24   -
25   - /**
26   - * 签名方式
27   - *
28   - * 例如说:MD5、SHA256
29   - */
30   - private String signMethod;
31   - /**
32   - * 固件文件签名
33   - */
34   - private String fileSign;
35   - /**
36   - * 固件文件大小
37   - */
38   - private Long fileSize;
39   - /**
40   - * 固件文件 URL
41   - */
42   - private String fileUrl;
43   -
44   - /**
45   - * 自定义信息,建议使用 JSON 格式
46   - */
47   - private String information;
48   -
49   - public static IotDeviceOtaUpgradeReqDTO build(Map<?, ?> map) {
50   - return new IotDeviceOtaUpgradeReqDTO()
51   - .setFirmwareId(MapUtil.getLong(map, "firmwareId")).setVersion((String) map.get("version"))
52   - .setSignMethod((String) map.get("signMethod")).setFileSign((String) map.get("fileSign"))
53   - .setFileSize(MapUtil.getLong(map, "fileSize")).setFileUrl((String) map.get("fileUrl"))
54   - .setInformation((String) map.get("information"));
55   - }
56   -
57   - public static Map<?, ?> build(IotDeviceOtaUpgradeReqDTO dto) {
58   - return MapUtil.builder()
59   - .put("firmwareId", dto.getFirmwareId()).put("version", dto.getVersion())
60   - .put("signMethod", dto.getSignMethod()).put("fileSign", dto.getFileSign())
61   - .put("fileSize", dto.getFileSize()).put("fileUrl", dto.getFileUrl())
62   - .put("information", dto.getInformation())
63   - .build();
64   - }
65   -
66   -}
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertyGetReqDTO.java deleted
1   -package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream;
2   -
3   -import jakarta.validation.constraints.NotEmpty;
4   -import lombok.Data;
5   -
6   -import java.util.List;
7   -
8   -// TODO @芋艿:从 server => plugin => device 是否有必要?从阿里云 iot 来看,没有这个功能?!
9   -// TODO @芋艿:是不是改成 read 更好?在看看阿里云的 topic 设计
10   -/**
11   - * IoT 设备【属性】获取 Request DTO
12   - *
13   - * @author 芋道源码
14   - */
15   -@Data
16   -public class IotDevicePropertyGetReqDTO extends IotDeviceDownstreamAbstractReqDTO {
17   -
18   - /**
19   - * 属性标识数组
20   - */
21   - @NotEmpty(message = "属性标识数组不能为空")
22   - private List<String> identifiers;
23   -
24   -}
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertySetReqDTO.java deleted
1   -package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream;
2   -
3   -import jakarta.validation.constraints.NotEmpty;
4   -import lombok.Data;
5   -
6   -import java.util.Map;
7   -
8   -/**
9   - * IoT 设备【属性】设置 Request DTO
10   - *
11   - * @author 芋道源码
12   - */
13   -@Data
14   -public class IotDevicePropertySetReqDTO extends IotDeviceDownstreamAbstractReqDTO {
15   -
16   - /**
17   - * 属性参数
18   - */
19   - @NotEmpty(message = "属性参数不能为空")
20   - private Map<String, Object> properties;
21   -
22   -}
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceServiceInvokeReqDTO.java deleted
1   -package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream;
2   -
3   -import jakarta.validation.constraints.NotEmpty;
4   -import lombok.Data;
5   -
6   -import java.util.Map;
7   -
8   -/**
9   - * IoT 设备【服务】调用 Request DTO
10   - *
11   - * @author 芋道源码
12   - */
13   -@Data
14   -public class IotDeviceServiceInvokeReqDTO extends IotDeviceDownstreamAbstractReqDTO {
15   -
16   - /**
17   - * 服务标识
18   - */
19   - @NotEmpty(message = "服务标识不能为空")
20   - private String identifier;
21   - /**
22   - * 调用参数
23   - */
24   - private Map<String, Object> params;
25   -
26   -}
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaProgressReqDTO.java deleted
1   -package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream;
2   -
3   -import lombok.Data;
4   -
5   -// TODO @芋艿:待实现:/ota/${productKey}/${deviceName}/progress
6   -/**
7   - * IoT 设备【OTA】升级进度 Request DTO(上报更新固件进度)
8   - *
9   - * @author 芋道源码
10   - */
11   -@Data
12   -public class IotDeviceOtaProgressReqDTO extends IotDeviceUpstreamAbstractReqDTO {
13   -
14   - /**
15   - * 固件编号
16   - */
17   - private Long firmwareId;
18   -
19   - /**
20   - * 升级状态
21   - *
22   - * 枚举 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum}
23   - */
24   - private Integer status;
25   - /**
26   - * 升级进度,百分比
27   - */
28   - private Integer progress;
29   -
30   - /**
31   - * 升级进度描述
32   - */
33   - private String description;
34   -
35   -}
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaPullReqDTO.java deleted
1   -package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream;
2   -
3   -// TODO @芋艿:待实现:/ota/${productKey}/${deviceName}/pull
4   -/**
5   - * IoT 设备【OTA】升级下拉 Request DTO(拉取固件更新)
6   - *
7   - * @author 芋道源码
8   - */
9   -public class IotDeviceOtaPullReqDTO {
10   -
11   - /**
12   - * 固件编号
13   - */
14   - private Long firmwareId;
15   -
16   - /**
17   - * 固件版本
18   - */
19   - private String version;
20   -
21   -}
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceOtaReportReqDTO.java deleted
1   -package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream;
2   -
3   -// TODO @芋艿:待实现:/ota/${productKey}/${deviceName}/report
4   -/**
5   - * IoT 设备【OTA】上报 Request DTO(上报固件版本)
6   - *
7   - * @author 芋道源码
8   - */
9   -public class IotDeviceOtaReportReqDTO {
10   -
11   - /**
12   - * 固件编号
13   - */
14   - private Long firmwareId;
15   -
16   - /**
17   - * 固件版本
18   - */
19   - private String version;
20   -
21   -}
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ApiConstants.java deleted
1   -package cn.iocoder.yudao.module.iot.enums;
2   -
3   -import cn.iocoder.yudao.framework.common.enums.RpcConstants;
4   -
5   -/**
6   - * API 相关的枚举
7   - *
8   - * @author 芋道源码
9   - */
10   -public class ApiConstants {
11   -
12   - public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/iot";
13   -
14   - public static final String VERSION = "1.0.0";
15   -
16   -}
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java
... ... @@ -15,8 +15,5 @@ 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   -
19   - public static final String IOT_DATA_BRIDGE_DIRECTION_ENUM = "iot_data_bridge_direction_enum";
20   - public static final String IOT_DATA_BRIDGE_TYPE_ENUM = "iot_data_bridge_type_enum";
21 18  
22 19 }
... ...
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java deleted
1   -package cn.iocoder.yudao.module.iot.enums.plugin;
2   -
3   -import cn.iocoder.yudao.framework.common.core.ArrayValuable;
4   -import lombok.Getter;
5   -import lombok.RequiredArgsConstructor;
6   -
7   -import java.util.Arrays;
8   -
9   -/**
10   - * IoT 部署方式枚举
11   - *
12   - * @author haohao
13   - */
14   -@RequiredArgsConstructor
15   -@Getter
16   -public enum IotPluginDeployTypeEnum implements ArrayValuable<Integer> {
17   -
18   - JAR(0, "JAR 部署"),
19   - STANDALONE(1, "独立部署");
20   -
21   - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotPluginDeployTypeEnum::getDeployType).toArray(Integer[]::new);
22   -
23   - /**
24   - * 部署方式
25   - */
26   - private final Integer deployType;
27   - /**
28   - * 部署方式名
29   - */
30   - private final String name;
31   -
32   - @Override
33   - public Integer[] array() {
34   - return ARRAYS;
35   - }
36   -
37   -}
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java deleted
1   -package cn.iocoder.yudao.module.iot.enums.plugin;
2   -
3   -import cn.iocoder.yudao.framework.common.core.ArrayValuable;
4   -import lombok.Getter;
5   -import lombok.RequiredArgsConstructor;
6   -
7   -import java.util.Arrays;
8   -
9   -/**
10   - * IoT 插件状态枚举
11   - *
12   - * @author haohao
13   - */
14   -@RequiredArgsConstructor
15   -@Getter
16   -public enum IotPluginStatusEnum implements ArrayValuable<Integer> {
17   -
18   - STOPPED(0, "停止"),
19   - RUNNING(1, "运行");
20   -
21   - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotPluginStatusEnum::getStatus).toArray(Integer[]::new);
22   -
23   - /**
24   - * 状态
25   - */
26   - private final Integer status;
27   - /**
28   - * 状态名
29   - */
30   - private final String name;
31   -
32   - @Override
33   - public Integer[] array() {
34   - return ARRAYS;
35   - }
36   -
37   -}
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java deleted
1   -package cn.iocoder.yudao.module.iot.enums.plugin;
2   -
3   -import cn.iocoder.yudao.framework.common.core.ArrayValuable;
4   -import lombok.AllArgsConstructor;
5   -import lombok.Getter;
6   -
7   -import java.util.Arrays;
8   -
9   -/**
10   - * IoT 插件类型枚举
11   - *
12   - * @author haohao
13   - */
14   -@AllArgsConstructor
15   -@Getter
16   -public enum IotPluginTypeEnum implements ArrayValuable<Integer> {
17   -
18   - NORMAL(0, "普通插件"),
19   - DEVICE(1, "设备插件");
20   -
21   - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotPluginTypeEnum::getType).toArray(Integer[]::new);
22   -
23   - /**
24   - * 类型
25   - */
26   - private final Integer type;
27   - /**
28   - * 类型名
29   - */
30   - private final String name;
31   -
32   - @Override
33   - public Integer[] array() {
34   - return ARRAYS;
35   - }
36   -
37   -}
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductScriptLanguageEnum.java deleted
1   -package cn.iocoder.yudao.module.iot.enums.product;
2   -
3   -import cn.iocoder.yudao.framework.common.core.ArrayValuable;
4   -import lombok.AllArgsConstructor;
5   -import lombok.Getter;
6   -
7   -import java.util.Arrays;
8   -
9   -/**
10   - * IoT 产品脚本语言枚举
11   - *
12   - * @author 芋道源码
13   - */
14   -@Getter
15   -@AllArgsConstructor
16   -public enum IotProductScriptLanguageEnum implements ArrayValuable<String> {
17   -
18   - JAVASCRIPT("javascript", "JavaScript"),
19   - JAVA("java", "Java"),
20   - PYTHON("python", "Python"),
21   - ;
22   -
23   - public static final String[] ARRAYS = Arrays.stream(values()).map(IotProductScriptLanguageEnum::getCode)
24   - .toArray(String[]::new);
25   -
26   - /**
27   - * 编码
28   - */
29   - private final String code;
30   - /**
31   - * 名称
32   - */
33   - private final String name;
34   -
35   - @Override
36   - public String[] array() {
37   - return ARRAYS;
38   - }
39   -
40   - public static IotProductScriptLanguageEnum getByCode(String code) {
41   - return Arrays.stream(values())
42   - .filter(type -> type.getCode().equals(code))
43   - .findFirst()
44   - .orElse(null);
45   - }
46   -
47   -}
48 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductScriptStatusEnum.java deleted
1   -package cn.iocoder.yudao.module.iot.enums.product;
2   -
3   -import cn.iocoder.yudao.framework.common.core.ArrayValuable;
4   -import lombok.AllArgsConstructor;
5   -import lombok.Getter;
6   -
7   -import java.util.Arrays;
8   -
9   -// TODO @haohao:要不复用 commonstatus?
10   -/**
11   - * IoT 产品脚本状态枚举
12   - *
13   - * @author 芋道源码
14   - */
15   -@Getter
16   -@AllArgsConstructor
17   -public enum IotProductScriptStatusEnum implements ArrayValuable<Integer> {
18   -
19   - ENABLE(0, "启用"),
20   - DISABLE(1, "禁用"),
21   - ;
22   -
23   - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotProductScriptStatusEnum::getStatus)
24   - .toArray(Integer[]::new);
25   -
26   - /**
27   - * 状态值
28   - */
29   - private final Integer status;
30   - /**
31   - * 状态名
32   - */
33   - private final String name;
34   -
35   - @Override
36   - public Integer[] array() {
37   - return ARRAYS;
38   - }
39   -
40   - public static IotProductScriptStatusEnum getByStatus(Integer status) {
41   - return Arrays.stream(values())
42   - .filter(type -> type.getStatus().equals(status))
43   - .findFirst()
44   - .orElse(null);
45   - }
46   -
47   - public static boolean isEnable(Integer status) {
48   - return ENABLE.getStatus().equals(status);
49   - }
50   -
51   - public static boolean isDisable(Integer status) {
52   - return DISABLE.getStatus().equals(status);
53   - }
54   -}
55 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductScriptTypeEnum.java deleted
1   -package cn.iocoder.yudao.module.iot.enums.product;
2   -
3   -import cn.iocoder.yudao.framework.common.core.ArrayValuable;
4   -import lombok.AllArgsConstructor;
5   -import lombok.Getter;
6   -
7   -import java.util.Arrays;
8   -
9   -/**
10   - * IoT 产品脚本类型枚举
11   - *
12   - * @author 芋道源码
13   - */
14   -@Getter
15   -@AllArgsConstructor
16   -public enum IotProductScriptTypeEnum implements ArrayValuable<Integer> {
17   -
18   - PROPERTY_PARSER(1, "property_parser", "属性解析"),
19   - EVENT_PARSER(2, "event_parser", "事件解析"),
20   - COMMAND_ENCODER(3, "command_encoder", "命令编码"),
21   - ;
22   -
23   - public static final Integer[] ARRAYS = Arrays.stream(values()).map(IotProductScriptTypeEnum::getCode)
24   - .toArray(Integer[]::new);
25   -
26   - /**
27   - * 编码
28   - */
29   - private final Integer code;
30   - /**
31   - * 类型
32   - */
33   - private final String type;
34   - /**
35   - * 名称
36   - */
37   - private final String name;
38   -
39   - @Override
40   - public Integer[] array() {
41   - return ARRAYS;
42   - }
43   -
44   - public static IotProductScriptTypeEnum getByCode(Integer code) {
45   - return Arrays.stream(values())
46   - .filter(type -> type.getCode().equals(code))
47   - .findFirst()
48   - .orElse(null);
49   - }
50   -}
51 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java
... ... @@ -55,7 +55,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
55 55 if (StrUtil.isEmpty(serverId)) {
56 56 throw exception(DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL);
57 57 }
58   - deviceMessageProducer.sendGatewayDeviceMessage(serverId, message);
  58 + deviceMessageProducer.sendDeviceMessageToGateway(serverId, message);
59 59  
60 60 // 3.2 发送给服务器(用于设备日志等的记录)
61 61 deviceMessageProducer.sendDeviceMessage(message);
... ...
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/package-info.java deleted
1   -package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt;
2 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-net-components/pom.xml deleted
1   -<?xml version="1.0" encoding="UTF-8"?>
2   -<project xmlns="http://maven.apache.org/POM/4.0.0"
3   - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4   - <parent>
5   - <artifactId>yudao-module-iot</artifactId>
6   - <groupId>cn.iocoder.boot</groupId>
7   - <version>${revision}</version>
8   - </parent>
9   - <modelVersion>4.0.0</modelVersion>
10   -
11   - <artifactId>yudao-module-iot-net-components</artifactId>
12   - <packaging>pom</packaging>
13   -
14   - <name>${project.artifactId}</name>
15   - <description>
16   - 物联网网络组件模块,提供与物联网设备通讯、管理的网络组件实现
17   - </description>
18   -
19   - <modules>
20   - <module>yudao-module-iot-net-component-core</module>
21   - <module>yudao-module-iot-net-component-emqx</module>
22   - <module>yudao-module-iot-net-component-server</module>
23   - </modules>
24   -
25   -</project>
26 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/pom.xml deleted
1   -<?xml version="1.0" encoding="UTF-8"?>
2   -<project xmlns="http://maven.apache.org/POM/4.0.0"
3   - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4   - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5   - <parent>
6   - <artifactId>yudao-module-iot-net-components</artifactId>
7   - <groupId>cn.iocoder.boot</groupId>
8   - <version>${revision}</version>
9   - </parent>
10   - <modelVersion>4.0.0</modelVersion>
11   -
12   - <artifactId>yudao-module-iot-net-component-core</artifactId>
13   - <packaging>jar</packaging>
14   -
15   - <name>${project.artifactId}</name>
16   - <description>
17   - 物联网网络组件核心模块
18   - </description>
19   -
20   - <dependencies>
21   - <dependency>
22   - <groupId>cn.iocoder.boot</groupId>
23   - <artifactId>yudao-module-iot-api</artifactId>
24   - <version>${revision}</version>
25   - </dependency>
26   -
27   - <dependency>
28   - <groupId>cn.iocoder.boot</groupId>
29   - <artifactId>yudao-module-iot-core</artifactId>
30   - <version>${revision}</version>
31   - </dependency>
32   -
33   - <!-- IoT 协议模块 -->
34   - <dependency>
35   - <groupId>cn.iocoder.boot</groupId>
36   - <artifactId>yudao-module-iot-protocol</artifactId>
37   - <version>${revision}</version>
38   - </dependency>
39   -
40   - <dependency>
41   - <groupId>org.springframework.boot</groupId>
42   - <artifactId>spring-boot-starter</artifactId>
43   - </dependency>
44   -
45   - <!-- Web 相关 -->
46   - <dependency>
47   - <groupId>org.springframework</groupId>
48   - <artifactId>spring-web</artifactId>
49   - </dependency>
50   -
51   - <!-- 工具类相关 -->
52   - <dependency>
53   - <groupId>io.vertx</groupId>
54   - <artifactId>vertx-web</artifactId>
55   - <optional>true</optional>
56   - </dependency>
57   -
58   - <!-- 参数校验 -->
59   - <dependency>
60   - <groupId>org.springframework.boot</groupId>
61   - <artifactId>spring-boot-starter-validation</artifactId>
62   - <optional>true</optional>
63   - </dependency>
64   - </dependencies>
65   -</project>
66 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/message/IotMqttMessage.java deleted
1   -package cn.iocoder.yudao.module.iot.net.component.core.message;
2   -
3   -import cn.hutool.core.util.IdUtil;
4   -import cn.hutool.json.JSONObject;
5   -import lombok.Builder;
6   -import lombok.Data;
7   -
8   -import java.util.Map;
9   -
10   -/**
11   - * IoT MQTT 消息模型
12   - * <p>
13   - * 基于 MQTT 协议规范实现的标准消息格式,兼容 Alink 协议
14   - *
15   - * @author haohao
16   - */
17   -@Data
18   -@Builder
19   -public class IotMqttMessage {
20   -
21   - /**
22   - * 消息 ID
23   - */
24   - private String id;
25   -
26   - /**
27   - * 协议版本
28   - */
29   - @Builder.Default
30   - private String version = "1.0";
31   -
32   - /**
33   - * 消息方法
34   - */
35   - private String method;
36   -
37   - /**
38   - * 消息参数
39   - */
40   - private Map<String, Object> params;
41   -
42   - /**
43   - * 转换为 JSONObject
44   - *
45   - * @return JSONObject 对象
46   - */
47   - public JSONObject toJsonObject() {
48   - JSONObject json = new JSONObject();
49   - json.set("id", id);
50   - json.set("version", version);
51   - json.set("method", method);
52   - json.set("params", params != null ? params : new JSONObject());
53   - return json;
54   - }
55   -
56   - /**
57   - * 转换为 JSON 字符串
58   - *
59   - * @return JSON 字符串
60   - */
61   - public String toJsonString() {
62   - return toJsonObject().toString();
63   - }
64   -
65   - /**
66   - * 创建设备服务调用消息
67   - *
68   - * @param requestId 请求 ID,为空时自动生成
69   - * @param serviceIdentifier 服务标识符
70   - * @param params 服务参数
71   - * @return MQTT 消息对象
72   - */
73   - public static IotMqttMessage createServiceInvokeMessage(String requestId, String serviceIdentifier,
74   - Map<String, Object> params) {
75   - return IotMqttMessage.builder()
76   - .id(requestId != null ? requestId : generateRequestId())
77   - .method("thing.service." + serviceIdentifier)
78   - .params(params)
79   - .build();
80   - }
81   -
82   - /**
83   - * 创建设备属性设置消息
84   - *
85   - * @param requestId 请求 ID,为空时自动生成
86   - * @param properties 设备属性
87   - * @return MQTT 消息对象
88   - */
89   - public static IotMqttMessage createPropertySetMessage(String requestId, Map<String, Object> properties) {
90   - return IotMqttMessage.builder()
91   - .id(requestId != null ? requestId : generateRequestId())
92   - .method("thing.service.property.set")
93   - .params(properties)
94   - .build();
95   - }
96   -
97   - /**
98   - * 创建设备属性获取消息
99   - *
100   - * @param requestId 请求 ID,为空时自动生成
101   - * @param identifiers 要获取的属性标识符列表
102   - * @return MQTT 消息对象
103   - */
104   - public static IotMqttMessage createPropertyGetMessage(String requestId, String[] identifiers) {
105   - JSONObject params = new JSONObject();
106   - params.set("identifiers", identifiers);
107   -
108   - return IotMqttMessage.builder()
109   - .id(requestId != null ? requestId : generateRequestId())
110   - .method("thing.service.property.get")
111   - .params(params)
112   - .build();
113   - }
114   -
115   - /**
116   - * 创建设备配置设置消息
117   - *
118   - * @param requestId 请求 ID,为空时自动生成
119   - * @param configs 设备配置
120   - * @return MQTT 消息对象
121   - */
122   - public static IotMqttMessage createConfigSetMessage(String requestId, Map<String, Object> configs) {
123   - return IotMqttMessage.builder()
124   - .id(requestId != null ? requestId : generateRequestId())
125   - .method("thing.service.config.set")
126   - .params(configs)
127   - .build();
128   - }
129   -
130   - /**
131   - * 创建设备 OTA 升级消息
132   - *
133   - * @param requestId 请求 ID,为空时自动生成
134   - * @param otaInfo OTA 升级信息
135   - * @return MQTT 消息对象
136   - */
137   - public static IotMqttMessage createOtaUpgradeMessage(String requestId, Map<String, Object> otaInfo) {
138   - return IotMqttMessage.builder()
139   - .id(requestId != null ? requestId : generateRequestId())
140   - .method("thing.service.ota.upgrade")
141   - .params(otaInfo)
142   - .build();
143   - }
144   -
145   - /**
146   - * 生成请求 ID
147   - *
148   - * @return 请求 ID
149   - */
150   - public static String generateRequestId() {
151   - return IdUtil.fastSimpleUUID();
152   - }
153   -}
154 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-core/src/main/java/cn/iocoder/yudao/module/iot/net/component/core/pojo/IotStandardResponse.java deleted
1   -package cn.iocoder.yudao.module.iot.net.component.core.pojo;
2   -
3   -import cn.hutool.core.util.StrUtil;
4   -import lombok.Data;
5   -
6   -/**
7   - * IoT 标准协议响应实体类
8   - * <p>
9   - * 用于统一 MQTT 和 HTTP 的响应格式
10   - *
11   - * @author haohao
12   - */
13   -@Data
14   -public class IotStandardResponse {
15   -
16   - /**
17   - * 消息 ID
18   - */
19   - private String id;
20   -
21   - /**
22   - * 状态码
23   - */
24   - private Integer code;
25   -
26   - /**
27   - * 响应数据
28   - */
29   - private Object data;
30   -
31   - /**
32   - * 响应消息
33   - */
34   - private String message;
35   -
36   - /**
37   - * 方法名
38   - */
39   - private String method;
40   -
41   - /**
42   - * 协议版本
43   - */
44   - private String version;
45   -
46   - /**
47   - * 创建成功响应
48   - *
49   - * @param id 消息 ID
50   - * @param method 方法名
51   - * @return 成功响应
52   - */
53   - public static IotStandardResponse success(String id, String method) {
54   - return success(id, method, null);
55   - }
56   -
57   - /**
58   - * 创建成功响应
59   - *
60   - * @param id 消息 ID
61   - * @param method 方法名
62   - * @param data 响应数据
63   - * @return 成功响应
64   - */
65   - public static IotStandardResponse success(String id, String method, Object data) {
66   - return new IotStandardResponse()
67   - .setId(id)
68   - .setCode(200)
69   - .setData(data)
70   - .setMessage("success")
71   - .setMethod(method)
72   - .setVersion("1.0");
73   - }
74   -
75   - /**
76   - * 创建错误响应
77   - *
78   - * @param id 消息 ID
79   - * @param method 方法名
80   - * @param code 错误码
81   - * @param message 错误消息
82   - * @return 错误响应
83   - */
84   - public static IotStandardResponse error(String id, String method, Integer code, String message) {
85   - return new IotStandardResponse()
86   - .setId(id)
87   - .setCode(code)
88   - .setData(null)
89   - .setMessage(StrUtil.blankToDefault(message, "error"))
90   - .setMethod(method)
91   - .setVersion("1.0");
92   - }
93   -}
94 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/pom.xml deleted
1   -<?xml version="1.0" encoding="UTF-8"?>
2   -<project xmlns="http://maven.apache.org/POM/4.0.0"
3   - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4   - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5   - <parent>
6   - <artifactId>yudao-module-iot-net-components</artifactId>
7   - <groupId>cn.iocoder.boot</groupId>
8   - <version>${revision}</version>
9   - </parent>
10   - <modelVersion>4.0.0</modelVersion>
11   - <artifactId>yudao-module-iot-net-component-emqx</artifactId>
12   - <packaging>jar</packaging>
13   -
14   - <name>${project.artifactId}</name>
15   - <description>
16   - 物联网网络组件 EMQX 模块
17   - </description>
18   -
19   - <dependencies>
20   - <dependency>
21   - <groupId>cn.iocoder.boot</groupId>
22   - <artifactId>yudao-module-iot-net-component-core</artifactId>
23   - <version>${revision}</version>
24   - </dependency>
25   -
26   - <!-- 工具类相关 -->
27   - <dependency>
28   - <groupId>io.vertx</groupId>
29   - <artifactId>vertx-web</artifactId>
30   - </dependency>
31   - <dependency>
32   - <groupId>io.vertx</groupId>
33   - <artifactId>vertx-mqtt</artifactId>
34   - </dependency>
35   -
36   - <!-- 参数校验 -->
37   - <dependency>
38   - <groupId>org.springframework.boot</groupId>
39   - <artifactId>spring-boot-starter-validation</artifactId>
40   - <optional>true</optional>
41   - </dependency>
42   - </dependencies>
43   -
44   -</project>
45 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/config/IotNetComponentEmqxAutoConfiguration.java deleted
1   -package cn.iocoder.yudao.module.iot.net.component.emqx.config;
2   -
3   -import cn.hutool.core.util.IdUtil;
4   -import cn.hutool.core.util.ObjUtil;
5   -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
6   -import cn.iocoder.yudao.module.iot.net.component.emqx.upstream.IotDeviceUpstreamServer;
7   -import io.vertx.core.Vertx;
8   -import io.vertx.mqtt.MqttClient;
9   -import io.vertx.mqtt.MqttClientOptions;
10   -import lombok.extern.slf4j.Slf4j;
11   -import org.springframework.beans.factory.annotation.Qualifier;
12   -import org.springframework.boot.autoconfigure.AutoConfiguration;
13   -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
14   -import org.springframework.boot.context.event.ApplicationStartedEvent;
15   -import org.springframework.boot.context.properties.EnableConfigurationProperties;
16   -import org.springframework.context.annotation.Bean;
17   -import org.springframework.context.annotation.ComponentScan;
18   -import org.springframework.context.event.EventListener;
19   -
20   -/**
21   - * IoT 网络组件 EMQX 的自动配置类
22   - *
23   - * @author haohao
24   - */
25   -@AutoConfiguration
26   -@EnableConfigurationProperties(IotNetComponentEmqxProperties.class)
27   -@ConditionalOnProperty(prefix = "yudao.iot.component.emqx", name = "enabled", havingValue = "true")
28   -@ComponentScan(basePackages = {
29   - "cn.iocoder.yudao.module.iot.net.component.emqx" // 只扫描 EMQX 组件包
30   -}) // TODO @haohao:自动配置后,不需要这个哈。
31   -@Slf4j
32   -public class IotNetComponentEmqxAutoConfiguration {
33   -
34   - /**
35   - * 初始化 EMQX 组件
36   - *
37   - * @param event 应用启动事件
38   - */
39   - @EventListener(ApplicationStartedEvent.class)
40   - public void initialize(ApplicationStartedEvent event) {
41   - log.info("[IotNetComponentEmqxAutoConfiguration][开始初始化]");
42   -
43   - // 从应用上下文中获取需要的 Bean
44   - // TODO @芋艿:看看要不要监听下
45   -
46   - log.info("[initialize][IoT EMQX 组件初始化完成]");
47   - }
48   -
49   - /**
50   - * 创建 Vert.x 实例
51   - */
52   - @Bean(name = "emqxVertx")
53   - public Vertx vertx() {
54   - return Vertx.vertx();
55   - }
56   -
57   - /**
58   - * 创建 MQTT 客户端
59   - */
60   - @Bean
61   - public MqttClient mqttClient(@Qualifier("emqxVertx") Vertx vertx, IotNetComponentEmqxProperties emqxProperties) {
62   - MqttClientOptions options = new MqttClientOptions()
63   - .setClientId("yudao-iot-downstream-" + IdUtil.fastSimpleUUID())
64   - .setUsername(emqxProperties.getMqttUsername())
65   - .setPassword(emqxProperties.getMqttPassword());
66   - // 设置 SSL 选项
67   - options.setSsl(ObjUtil.defaultIfNull(emqxProperties.getMqttSsl(), false));
68   - return MqttClient.create(vertx, options);
69   - }
70   -
71   - /**
72   - * 创建设备上行服务器
73   - */
74   - @Bean(name = "emqxDeviceUpstreamServer", initMethod = "start", destroyMethod = "stop")
75   - public IotDeviceUpstreamServer deviceUpstreamServer(
76   - IotDeviceUpstreamApi deviceUpstreamApi,
77   - IotNetComponentEmqxProperties emqxProperties,
78   - @Qualifier("emqxVertx") Vertx vertx,
79   - MqttClient mqttClient) {
80   - return new IotDeviceUpstreamServer(emqxProperties, deviceUpstreamApi, vertx, mqttClient);
81   - }
82   -
83   -}
84 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/config/IotNetComponentEmqxProperties.java deleted
1   -package cn.iocoder.yudao.module.iot.net.component.emqx.config;
2   -
3   -import jakarta.validation.constraints.NotBlank;
4   -import jakarta.validation.constraints.NotEmpty;
5   -import jakarta.validation.constraints.NotNull;
6   -import lombok.Data;
7   -import org.springframework.boot.context.properties.ConfigurationProperties;
8   -import org.springframework.validation.annotation.Validated;
9   -
10   -/**
11   - * IoT EMQX 网络组件配置属性
12   - *
13   - * @author haohao
14   - */
15   -@ConfigurationProperties(prefix = "yudao.iot.component.emqx")
16   -@Data
17   -@Validated
18   -public class IotNetComponentEmqxProperties {
19   -
20   - /**
21   - * 是否启用 EMQX 组件
22   - */
23   - private Boolean enabled;
24   -
25   - /**
26   - * MQTT 服务主机
27   - */
28   - @NotBlank(message = "MQTT 服务器主机不能为空")
29   - private String mqttHost;
30   -
31   - /**
32   - * MQTT 服务端口
33   - */
34   - @NotNull(message = "MQTT 服务器端口不能为空")
35   - private Integer mqttPort;
36   -
37   - /**
38   - * MQTT 服务用户名
39   - */
40   - @NotBlank(message = "MQTT 服务器用户名不能为空")
41   - private String mqttUsername;
42   -
43   - /**
44   - * MQTT 服务密码
45   - */
46   - @NotBlank(message = "MQTT 服务器密码不能为空")
47   - private String mqttPassword;
48   -
49   - /**
50   - * 是否启用 SSL
51   - */
52   - @NotNull(message = "MQTT SSL 配置不能为空")
53   - private Boolean mqttSsl;
54   -
55   - /**
56   - * 订阅的主题列表
57   - */
58   - @NotEmpty(message = "MQTT 订阅主题不能为空")
59   - private String[] mqttTopics;
60   -
61   - /**
62   - * 认证端口
63   - */
64   - @NotNull(message = "认证端口不能为空")
65   - private Integer authPort;
66   -
67   - // TODO @haohao:可以使用 Duration 类型,可读性更好
68   - /**
69   - * 重连延迟时间(毫秒)
70   - * <p>
71   - * 默认值:5000 毫秒
72   - */
73   - private Integer reconnectDelayMs = 5000;
74   -
75   - /**
76   - * 连接超时时间(毫秒)
77   - * <p>
78   - * 默认值:10000 毫秒
79   - */
80   - private Integer connectionTimeoutMs = 10000;
81   -
82   -}
83 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/downstream/IotDeviceDownstreamHandlerImpl.java deleted
1   -package cn.iocoder.yudao.module.iot.net.component.emqx.downstream;
2   -
3   -// TODO @芋艿:后续再支持下;@haohao;改成消费者
4   -///**
5   -// * EMQX 网络组件的 {@link IotDeviceDownstreamHandler} 实现类
6   -// *
7   -// * @author 芋道源码
8   -// */
9   -//@Slf4j
10   -//public class IotDeviceDownstreamHandlerImpl {
11   -//
12   -// /**
13   -// * MQTT 客户端
14   -// */
15   -// private final MqttClient mqttClient;
16   -//
17   -// /**
18   -// * 构造函数
19   -// *
20   -// * @param mqttClient MQTT 客户端
21   -// */
22   -// public IotDeviceDownstreamHandlerImpl(MqttClient mqttClient) {
23   -// this.mqttClient = mqttClient;
24   -// }
25   -//
26   -// @Override
27   -// public CommonResult<Boolean> invokeDeviceService(IotDeviceServiceInvokeReqDTO reqDTO) {
28   -// log.info("[invokeService][开始调用设备服务][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO));
29   -//
30   -// // 验证参数
31   -// if (reqDTO.getProductKey() == null || reqDTO.getDeviceName() == null || reqDTO.getIdentifier() == null) {
32   -// log.error("[invokeService][参数不完整][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO));
33   -// return CommonResult.error(MQTT_TOPIC_ILLEGAL.getCode(), MQTT_TOPIC_ILLEGAL.getMsg());
34   -// }
35   -//
36   -// try {
37   -// // 构建请求主题
38   -// String topic = IotDeviceTopicEnum.buildServiceTopic(reqDTO.getProductKey(), reqDTO.getDeviceName(),
39   -// reqDTO.getIdentifier());
40   -//
41   -// // 构建请求消息
42   -// String requestId = StrUtil.isNotEmpty(reqDTO.getRequestId()) ? reqDTO.getRequestId()
43   -// : IotNetComponentCommonUtils.generateRequestId();
44   -// IotMqttMessage message = IotMqttMessage.createServiceInvokeMessage(
45   -// requestId, reqDTO.getIdentifier(), reqDTO.getParams());
46   -//
47   -// // 发送消息
48   -// publishMessage(topic, message.toJsonObject());
49   -//
50   -// log.info("[invokeService][调用设备服务成功][requestId: {}][topic: {}]", requestId, topic);
51   -// return CommonResult.success(true);
52   -// } catch (Exception e) {
53   -// log.error("[invokeService][调用设备服务异常][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO), e);
54   -// return CommonResult.error(MQTT_TOPIC_ILLEGAL.getCode(), MQTT_TOPIC_ILLEGAL.getMsg());
55   -// }
56   -// }
57   -//
58   -// @Override
59   -// public CommonResult<Boolean> getDeviceProperty(IotDevicePropertyGetReqDTO getReqDTO) {
60   -// // 暂未实现,返回成功
61   -// return CommonResult.success(true);
62   -// }
63   -//
64   -// @Override
65   -// public CommonResult<Boolean> setDeviceProperty(IotDevicePropertySetReqDTO reqDTO) {
66   -// log.info("[setProperty][开始设置设备属性][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO));
67   -//
68   -// // 验证参数
69   -// if (reqDTO.getProductKey() == null || reqDTO.getDeviceName() == null) {
70   -// log.error("[setProperty][参数不完整][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO));
71   -// return CommonResult.error(MQTT_TOPIC_ILLEGAL.getCode(), MQTT_TOPIC_ILLEGAL.getMsg());
72   -// }
73   -//
74   -// try {
75   -// // 构建请求主题
76   -// String topic = IotDeviceTopicEnum.buildPropertySetTopic(reqDTO.getProductKey(), reqDTO.getDeviceName());
77   -//
78   -// // 构建请求消息
79   -// String requestId = StrUtil.isNotEmpty(reqDTO.getRequestId()) ? reqDTO.getRequestId()
80   -// : IotNetComponentCommonUtils.generateRequestId();
81   -// IotMqttMessage message = IotMqttMessage.createPropertySetMessage(requestId, reqDTO.getProperties());
82   -//
83   -// // 发送消息
84   -// publishMessage(topic, message.toJsonObject());
85   -//
86   -// log.info("[setProperty][设置设备属性成功][requestId: {}][topic: {}]", requestId, topic);
87   -// return CommonResult.success(true);
88   -// } catch (Exception e) {
89   -// log.error("[setProperty][设置设备属性异常][reqDTO: {}]", JSONUtil.toJsonStr(reqDTO), e);
90   -// return CommonResult.error(MQTT_TOPIC_ILLEGAL.getCode(), MQTT_TOPIC_ILLEGAL.getMsg());
91   -// }
92   -// }
93   -//
94   -// @Override
95   -// public CommonResult<Boolean> setDeviceConfig(IotDeviceConfigSetReqDTO setReqDTO) {
96   -// // 暂未实现,返回成功
97   -// return CommonResult.success(true);
98   -// }
99   -//
100   -// @Override
101   -// public CommonResult<Boolean> upgradeDeviceOta(IotDeviceOtaUpgradeReqDTO upgradeReqDTO) {
102   -// // 暂未实现,返回成功
103   -// return CommonResult.success(true);
104   -// }
105   -//
106   -// /**
107   -// * 发布 MQTT 消息
108   -// *
109   -// * @param topic 主题
110   -// * @param payload 消息内容
111   -// */
112   -// private void publishMessage(String topic, JSONObject payload) {
113   -// mqttClient.publish(
114   -// topic,
115   -// Buffer.buffer(payload.toString()),
116   -// MqttQoS.AT_LEAST_ONCE,
117   -// false,
118   -// false);
119   -// log.info("[publishMessage][发送消息成功][topic: {}][payload: {}]", topic, payload);
120   -// }
121   -//}
122 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/IotDeviceUpstreamServer.java deleted
1   -package cn.iocoder.yudao.module.iot.net.component.emqx.upstream;
2   -
3   -import cn.hutool.core.util.ArrayUtil;
4   -import cn.hutool.core.util.StrUtil;
5   -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
6   -import cn.iocoder.yudao.module.iot.net.component.emqx.config.IotNetComponentEmqxProperties;
7   -import cn.iocoder.yudao.module.iot.net.component.emqx.upstream.router.IotDeviceAuthVertxHandler;
8   -import cn.iocoder.yudao.module.iot.net.component.emqx.upstream.router.IotDeviceMqttMessageHandler;
9   -import cn.iocoder.yudao.module.iot.net.component.emqx.upstream.router.IotDeviceWebhookVertxHandler;
10   -import io.netty.handler.codec.mqtt.MqttQoS;
11   -import io.vertx.core.Future;
12   -import io.vertx.core.Vertx;
13   -import io.vertx.core.http.HttpServer;
14   -import io.vertx.ext.web.Router;
15   -import io.vertx.ext.web.handler.BodyHandler;
16   -import io.vertx.mqtt.MqttClient;
17   -import lombok.extern.slf4j.Slf4j;
18   -
19   -import java.util.concurrent.CompletableFuture;
20   -import java.util.concurrent.TimeUnit;
21   -
22   -/**
23   - * IoT 设备上行服务端,接收来自 device 设备的请求,转发给 server 服务器
24   - * <p>
25   - * 协议:HTTP、MQTT
26   - *
27   - * @author haohao
28   - */
29   -@Slf4j
30   -public class IotDeviceUpstreamServer {
31   -
32   - /**
33   - * 默认 QoS 级别
34   - */
35   - private static final MqttQoS DEFAULT_QOS = MqttQoS.AT_LEAST_ONCE;
36   -
37   - private final Vertx vertx;
38   - private final HttpServer server;
39   - private final MqttClient client;
40   - private final IotNetComponentEmqxProperties emqxProperties;
41   - private final IotDeviceMqttMessageHandler mqttMessageHandler;
42   -
43   - /**
44   - * 服务运行状态标志
45   - */
46   - private volatile boolean isRunning = false;
47   -
48   - public IotDeviceUpstreamServer(IotNetComponentEmqxProperties emqxProperties,
49   - IotDeviceUpstreamApi deviceUpstreamApi,
50   - Vertx vertx,
51   - MqttClient client) {
52   - this.vertx = vertx;
53   - this.emqxProperties = emqxProperties;
54   - this.client = client;
55   -
56   - // 创建 Router 实例
57   - Router router = Router.router(vertx);
58   - router.route().handler(BodyHandler.create()); // 处理 Body
59   - router.post(IotDeviceAuthVertxHandler.PATH)
60   - // MQTT 认证不必须通过 HTTP 进行,但 HTTP 认证是 EMQX 等 MQTT 服务器支持的一种灵活的认证方式
61   - .handler(new IotDeviceAuthVertxHandler(deviceUpstreamApi));
62   - // 添加 Webhook 处理器,用于处理设备连接和断开连接事件
63   - router.post(IotDeviceWebhookVertxHandler.PATH)
64   - .handler(new IotDeviceWebhookVertxHandler(deviceUpstreamApi));
65   - // 创建 HttpServer 实例
66   - this.server = vertx.createHttpServer().requestHandler(router);
67   - this.mqttMessageHandler = new IotDeviceMqttMessageHandler(deviceUpstreamApi, client);
68   - }
69   -
70   - /**
71   - * 启动 HTTP 服务器、MQTT 客户端
72   - */
73   - public void start() {
74   - if (isRunning) {
75   - log.warn("[start][服务已经在运行中,请勿重复启动]");
76   - return;
77   - }
78   - log.info("[start][开始启动服务]");
79   -
80   - // 检查 authPort 是否为 null
81   - // TODO @haohao:authPort 里面搞默认值?包括下面,这个类不搞任何默认值,都交给 emqxProperties
82   - Integer authPort = emqxProperties.getAuthPort();
83   - if (authPort == null) {
84   - log.warn("[start][authPort 为 null,使用默认端口 8080]");
85   - authPort = 8080; // 默认端口
86   - }
87   -
88   - // 获取连接超时时间
89   - int connectionTimeoutMs = emqxProperties.getConnectionTimeoutMs() != null
90   - ? emqxProperties.getConnectionTimeoutMs()
91   - : 10000;
92   -
93   - // 1. 启动 HTTP 服务器
94   - final Integer finalAuthPort = authPort; // 为 lambda 表达式创建 final 变量
95   - CompletableFuture<Void> httpFuture = server.listen(finalAuthPort)
96   - .toCompletionStage()
97   - .toCompletableFuture()
98   - .thenAccept(v -> log.info("[start][HTTP 服务器启动完成,端口: {}]", server.actualPort()));
99   -
100   - // 2. 连接 MQTT Broker
101   - CompletableFuture<Void> mqttFuture = connectMqtt()
102   - .toCompletionStage()
103   - .toCompletableFuture()
104   - .thenAccept(v -> {
105   - // 2.1 添加 MQTT 断开重连监听器
106   - client.closeHandler(closeEvent -> {
107   - log.warn("[closeHandler][MQTT 连接已断开,准备重连]");
108   - reconnectWithDelay();
109   - });
110   - // 2.2 设置 MQTT 消息处理器
111   - setupMessageHandler();
112   - });
113   -
114   - // 3. 等待所有服务启动完成
115   - CompletableFuture.allOf(httpFuture, mqttFuture)
116   - .orTimeout(connectionTimeoutMs, TimeUnit.MILLISECONDS)
117   - .whenComplete((result, error) -> {
118   - if (error != null) {
119   - log.error("[start][服务启动失败]", error);
120   - } else {
121   - isRunning = true;
122   - log.info("[start][所有服务启动完成]");
123   - }
124   - });
125   - }
126   -
127   - /**
128   - * 设置 MQTT 消息处理器
129   - */
130   - private void setupMessageHandler() {
131   - client.publishHandler(mqttMessageHandler::handle);
132   - log.debug("[setupMessageHandler][MQTT 消息处理器设置完成]");
133   - }
134   -
135   - /**
136   - * 重连 MQTT 客户端
137   - */
138   - private void reconnectWithDelay() {
139   - if (!isRunning) {
140   - log.info("[reconnectWithDelay][服务已停止,不再尝试重连]");
141   - return;
142   - }
143   -
144   - // 获取重连延迟时间
145   - int reconnectDelayMs = emqxProperties.getReconnectDelayMs() != null
146   - ? emqxProperties.getReconnectDelayMs()
147   - : 5000;
148   -
149   - vertx.setTimer(reconnectDelayMs, id -> {
150   - log.info("[reconnectWithDelay][开始重新连接 MQTT]");
151   - connectMqtt();
152   - });
153   - }
154   -
155   - /**
156   - * 连接 MQTT Broker 并订阅主题
157   - *
158   - * @return 连接结果的 Future
159   - */
160   - private Future<Void> connectMqtt() {
161   - // 检查必要的 MQTT 配置
162   - String host = emqxProperties.getMqttHost();
163   - Integer port = emqxProperties.getMqttPort();
164   - if (StrUtil.isBlank(host)) {
165   - String msg = "[connectMqtt][MQTT Host 为空,无法连接]";
166   - log.error(msg);
167   - return Future.failedFuture(new IllegalStateException(msg));
168   - }
169   - if (port == null) {
170   - log.warn("[connectMqtt][MQTT Port 为 null,使用默认端口 1883]");
171   - port = 1883; // 默认 MQTT 端口
172   - }
173   -
174   - final Integer finalPort = port;
175   - return client.connect(finalPort, host)
176   - .compose(connAck -> {
177   - log.info("[connectMqtt][MQTT 客户端连接成功]");
178   - return subscribeToTopics();
179   - })
180   - .recover(error -> {
181   - log.error("[connectMqtt][连接 MQTT Broker 失败:]", error);
182   - reconnectWithDelay();
183   - return Future.failedFuture(error);
184   - });
185   - }
186   -
187   - /**
188   - * 订阅设备上行消息主题
189   - *
190   - * @return 订阅结果的 Future
191   - */
192   - private Future<Void> subscribeToTopics() {
193   - String[] topics = emqxProperties.getMqttTopics();
194   - if (ArrayUtil.isEmpty(topics)) {
195   - log.warn("[subscribeToTopics][未配置 MQTT 主题或为 null,使用默认主题]");
196   - topics = new String[]{"/device/#"}; // 默认订阅所有设备上下行主题
197   - }
198   -
199   - // 使用协调器追踪多个 Future 的完成状态
200   - Future<Void> result = Future.succeededFuture();
201   - for (String topic : topics) {
202   - if (StrUtil.isBlank(topic)) {
203   - log.warn("[subscribeToTopics][跳过空主题]");
204   - continue;
205   - }
206   -
207   - result = result.compose(v -> client.subscribe(topic, DEFAULT_QOS.value())
208   - .<Void>map(ack -> {
209   - log.info("[subscribeToTopics][订阅主题成功: {}]", topic);
210   - return null;
211   - })
212   - .recover(err -> {
213   - log.error("[subscribeToTopics][订阅主题失败: {}]", topic, err);
214   - return Future.failedFuture(err);
215   - }));
216   - }
217   - return result;
218   - }
219   -
220   - /**
221   - * 停止服务
222   - */
223   - public void stop() {
224   - if (!isRunning) {
225   - log.warn("[stop][服务已经停止,无需再次停止]");
226   - return;
227   - }
228   - log.info("[stop][开始停止服务]");
229   -
230   - // 1. 取消 MQTT 主题订阅
231   - if (client.isConnected()) {
232   - for (String topic : emqxProperties.getMqttTopics()) {
233   - try {
234   - client.unsubscribe(topic);
235   - } catch (Exception e) {
236   - log.warn("[stop][取消订阅主题异常: {}]", topic, e);
237   - }
238   - }
239   - }
240   -
241   - // 2. 关闭 MQTT 客户端
242   - try {
243   - if (client.isConnected()) {
244   - client.disconnect();
245   - }
246   - } catch (Exception e) {
247   - log.warn("[stop][关闭 MQTT 客户端异常]", e);
248   - }
249   -
250   - // 3. 关闭 HTTP 服务器
251   - try {
252   - server.close();
253   - } catch (Exception e) {
254   - log.warn("[stop][关闭 HTTP 服务器异常]", e);
255   - }
256   -
257   - // 4. 更新状态
258   - isRunning = false;
259   - log.info("[stop][服务已停止]");
260   - }
261   -}
262 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/router/IotDeviceAuthVertxHandler.java deleted
1   -package cn.iocoder.yudao.module.iot.net.component.emqx.upstream.router;
2   -
3   -import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4   -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
5   -import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceEmqxAuthReqDTO;
6   -import cn.iocoder.yudao.module.iot.net.component.core.util.IotNetComponentCommonUtils;
7   -import io.vertx.core.Handler;
8   -import io.vertx.core.json.JsonObject;
9   -import io.vertx.ext.web.RoutingContext;
10   -import lombok.RequiredArgsConstructor;
11   -import lombok.extern.slf4j.Slf4j;
12   -
13   -import java.util.Collections;
14   -
15   -/**
16   - * IoT EMQX 连接认证的 Vert.x Handler
17   - * <p>
18   - * 参考:<a href="https://docs.emqx.com/zh/emqx/latest/access-control/authn/http.html">EMQX HTTP</a>
19   - * <p>
20   - * 注意:该处理器需要返回特定格式:{"result": "allow"} 或 {"result": "deny"},
21   - * 以符合 EMQX 认证插件的要求,因此不使用 IotStandardResponse 实体类
22   - *
23   - * @author haohao
24   - */
25   -@RequiredArgsConstructor
26   -@Slf4j
27   -public class IotDeviceAuthVertxHandler implements Handler<RoutingContext> {
28   -
29   - public static final String PATH = "/mqtt/auth";
30   -
31   - private final IotDeviceUpstreamApi deviceUpstreamApi;
32   -
33   - @Override
34   - public void handle(RoutingContext routingContext) {
35   - try {
36   - // 构建认证请求 DTO
37   - JsonObject json = routingContext.body().asJsonObject();
38   - String clientId = json.getString("clientid");
39   - String username = json.getString("username");
40   - String password = json.getString("password");
41   - IotDeviceEmqxAuthReqDTO authReqDTO = new IotDeviceEmqxAuthReqDTO()
42   - .setClientId(clientId)
43   - .setUsername(username)
44   - .setPassword(password);
45   -
46   - // 调用认证 API
47   - CommonResult<Boolean> authResult = deviceUpstreamApi.authenticateEmqxConnection(authReqDTO);
48   - if (authResult.getCode() != 0 || !authResult.getData()) {
49   - // 注意:这里必须返回 {"result": "deny"} 格式,以符合 EMQX 认证插件的要求
50   - IotNetComponentCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "deny"));
51   - return;
52   - }
53   -
54   - // 响应结果
55   - // 注意:这里必须返回 {"result": "allow"} 格式,以符合 EMQX 认证插件的要求
56   - IotNetComponentCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "allow"));
57   - } catch (Exception e) {
58   - log.error("[handle][EMQX 认证异常]", e);
59   - // 注意:这里必须返回 {"result": "deny"} 格式,以符合 EMQX 认证插件的要求
60   - IotNetComponentCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "deny"));
61   - }
62   - }
63   -
64   -}
65 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/router/IotDeviceMqttMessageHandler.java deleted
1   -package cn.iocoder.yudao.module.iot.net.component.emqx.upstream.router;
2   -
3   -import cn.hutool.core.util.StrUtil;
4   -import cn.hutool.json.JSONObject;
5   -import cn.hutool.json.JSONUtil;
6   -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
7   -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
8   -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO;
9   -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO;
10   -import cn.iocoder.yudao.module.iot.net.component.core.constants.IotDeviceTopicEnum;
11   -import cn.iocoder.yudao.module.iot.net.component.core.pojo.IotStandardResponse;
12   -import cn.iocoder.yudao.module.iot.net.component.core.util.IotNetComponentCommonUtils;
13   -import io.netty.handler.codec.mqtt.MqttQoS;
14   -import io.vertx.core.buffer.Buffer;
15   -import io.vertx.mqtt.MqttClient;
16   -import io.vertx.mqtt.messages.MqttPublishMessage;
17   -import lombok.extern.slf4j.Slf4j;
18   -
19   -import java.time.LocalDateTime;
20   -import java.util.Arrays;
21   -import java.util.HashMap;
22   -import java.util.Map;
23   -
24   -/**
25   - * IoT 设备 MQTT 消息处理器
26   - * <p>
27   - * 参考:<a href=
28   - * "https://help.aliyun.com/zh/iot/user-guide/device-properties-events-and-services">设备属性、事件、服务</a>
29   - */
30   -@Slf4j
31   -public class IotDeviceMqttMessageHandler {
32   -
33   - // TODO @haohao:下面的,有办法也抽到 IotDeviceTopicEnum 么?想的是,尽量把这些 method、topic、url 统一化;
34   - private static final String PROPERTY_METHOD = "thing.event.property.post";
35   - private static final String EVENT_METHOD_PREFIX = "thing.event.";
36   - private static final String EVENT_METHOD_SUFFIX = ".post";
37   -
38   - private final IotDeviceUpstreamApi deviceUpstreamApi;
39   - private final MqttClient mqttClient;
40   -
41   - public IotDeviceMqttMessageHandler(IotDeviceUpstreamApi deviceUpstreamApi, MqttClient mqttClient) {
42   - this.deviceUpstreamApi = deviceUpstreamApi;
43   - this.mqttClient = mqttClient;
44   - }
45   -
46   - /**
47   - * 处理MQTT消息
48   - *
49   - * @param message MQTT发布消息
50   - */
51   - public void handle(MqttPublishMessage message) {
52   - String topic = message.topicName();
53   - String payload = message.payload().toString();
54   - log.info("[messageHandler][接收到消息][topic: {}][payload: {}]", topic, payload);
55   -
56   - try {
57   - if (StrUtil.isEmpty(payload)) {
58   - log.warn("[messageHandler][消息内容为空][topic: {}]", topic);
59   - return;
60   - }
61   - handleMessage(topic, payload);
62   - } catch (Exception e) {
63   - log.error("[messageHandler][处理消息失败][topic: {}][payload: {}]", topic, payload, e);
64   - }
65   - }
66   -
67   - /**
68   - * 根据主题类型处理消息
69   - *
70   - * @param topic 主题
71   - * @param payload 消息内容
72   - */
73   - private void handleMessage(String topic, String payload) {
74   - // 校验前缀
75   - if (!topic.startsWith(IotDeviceTopicEnum.SYS_TOPIC_PREFIX.getTopic())) {
76   - log.warn("[handleMessage][未知的消息类型][topic: {}]", topic);
77   - return;
78   - }
79   -
80   - // 处理设备属性上报消息
81   - if (topic.endsWith(IotDeviceTopicEnum.PROPERTY_POST_TOPIC.getTopic())) {
82   - log.info("[handleMessage][接收到设备属性上报][topic: {}]", topic);
83   - handlePropertyPost(topic, payload);
84   - return;
85   - }
86   -
87   - // 处理设备事件上报消息
88   - if (topic.contains(IotDeviceTopicEnum.EVENT_POST_TOPIC_PREFIX.getTopic()) &&
89   - topic.endsWith(IotDeviceTopicEnum.EVENT_POST_TOPIC_SUFFIX.getTopic())) {
90   - log.info("[handleMessage][接收到设备事件上报][topic: {}]", topic);
91   - handleEventPost(topic, payload);
92   - return;
93   - }
94   -
95   - // 未知消息类型
96   - log.warn("[handleMessage][未知的消息类型][topic: {}]", topic);
97   - }
98   -
99   - /**
100   - * 处理设备属性上报消息
101   - *
102   - * @param topic 主题
103   - * @param payload 消息内容
104   - */
105   - private void handlePropertyPost(String topic, String payload) {
106   - try {
107   - // 解析消息内容
108   - JSONObject jsonObject = JSONUtil.parseObj(payload);
109   - String[] topicParts = parseTopic(topic);
110   - if (topicParts == null) {
111   - return;
112   - }
113   -
114   - // 构建设备属性上报请求对象
115   - IotDevicePropertyReportReqDTO reportReqDTO = buildPropertyReportDTO(jsonObject, topicParts);
116   -
117   - // 调用上游 API 处理设备上报数据
118   - deviceUpstreamApi.reportDeviceProperty(reportReqDTO);
119   - log.info("[handlePropertyPost][处理设备属性上报成功][topic: {}]", topic);
120   -
121   - // 发送响应消息
122   - sendResponse(topic, jsonObject, PROPERTY_METHOD, null);
123   - } catch (Exception e) {
124   - log.error("[handlePropertyPost][处理设备属性上报失败][topic: {}][payload: {}]", topic, payload, e);
125   - }
126   - }
127   -
128   - /**
129   - * 处理设备事件上报消息
130   - *
131   - * @param topic 主题
132   - * @param payload 消息内容
133   - */
134   - private void handleEventPost(String topic, String payload) {
135   - try {
136   - // 解析消息内容
137   - JSONObject jsonObject = JSONUtil.parseObj(payload);
138   - String[] topicParts = parseTopic(topic);
139   - if (topicParts == null) {
140   - return;
141   - }
142   -
143   - // 构建设备事件上报请求对象
144   - IotDeviceEventReportReqDTO reportReqDTO = buildEventReportDTO(jsonObject, topicParts);
145   -
146   - // 调用上游 API 处理设备上报数据
147   - deviceUpstreamApi.reportDeviceEvent(reportReqDTO);
148   - log.info("[handleEventPost][处理设备事件上报成功][topic: {}]", topic);
149   -
150   - // 从 topic 中获取事件标识符
151   - String eventIdentifier = getEventIdentifier(topicParts, topic);
152   - if (eventIdentifier == null) {
153   - return;
154   - }
155   -
156   - // 发送响应消息
157   - String method = EVENT_METHOD_PREFIX + eventIdentifier + EVENT_METHOD_SUFFIX;
158   - sendResponse(topic, jsonObject, method, null);
159   - } catch (Exception e) {
160   - log.error("[handleEventPost][处理设备事件上报失败][topic: {}][payload: {}]", topic, payload, e);
161   - }
162   - }
163   -
164   - /**
165   - * 解析主题,获取主题各部分
166   - *
167   - * @param topic 主题
168   - * @return 主题各部分数组,如果解析失败返回null
169   - */
170   - private String[] parseTopic(String topic) {
171   - String[] topicParts = topic.split("/");
172   - if (topicParts.length < 7) {
173   - log.warn("[parseTopic][主题格式不正确][topic: {}]", topic);
174   - return null;
175   - }
176   - return topicParts;
177   - }
178   -
179   - /**
180   - * 从主题部分中获取事件标识符
181   - *
182   - * @param topicParts 主题各部分
183   - * @param topic 原始主题,用于日志
184   - * @return 事件标识符,如果获取失败返回null
185   - */
186   - private String getEventIdentifier(String[] topicParts, String topic) {
187   - try {
188   - return topicParts[6];
189   - } catch (ArrayIndexOutOfBoundsException e) {
190   - log.warn("[getEventIdentifier][无法从主题中获取事件标识符][topic: {}][topicParts: {}]",
191   - topic, Arrays.toString(topicParts));
192   - return null;
193   - }
194   - }
195   -
196   - /**
197   - * 发送响应消息
198   - *
199   - * @param topic 原始主题
200   - * @param jsonObject 原始消息JSON对象
201   - * @param method 响应方法
202   - * @param customData 自定义数据,可为 null
203   - */
204   - private void sendResponse(String topic, JSONObject jsonObject, String method, Object customData) {
205   - String replyTopic = IotDeviceTopicEnum.getReplyTopic(topic);
206   -
207   - // 响应结果
208   - IotStandardResponse response = IotStandardResponse.success(
209   - jsonObject.getStr("id"), method, customData);
210   - try {
211   - mqttClient.publish(replyTopic, Buffer.buffer(JsonUtils.toJsonString(response)),
212   - MqttQoS.AT_LEAST_ONCE, false, false);
213   - log.info("[sendResponse][发送响应消息成功][topic: {}]", replyTopic);
214   - } catch (Exception e) {
215   - log.error("[sendResponse][发送响应消息失败][topic: {}][response: {}]", replyTopic, response, e);
216   - }
217   - }
218   -
219   - /**
220   - * 构建设备属性上报请求对象
221   - *
222   - * @param jsonObject 消息内容
223   - * @param topicParts 主题部分
224   - * @return 设备属性上报请求对象
225   - */
226   - private IotDevicePropertyReportReqDTO buildPropertyReportDTO(JSONObject jsonObject, String[] topicParts) {
227   - // TODO @haohao:IotDevicePropertyReportReqDTO 可以考虑链式哈。其它也是,尽量让同类参数在一行;这样,阅读起来更聚焦;
228   - IotDevicePropertyReportReqDTO reportReqDTO = new IotDevicePropertyReportReqDTO();
229   - reportReqDTO.setRequestId(jsonObject.getStr("id"));
230   - reportReqDTO.setProcessId(IotNetComponentCommonUtils.getProcessId());
231   - reportReqDTO.setReportTime(LocalDateTime.now());
232   - reportReqDTO.setProductKey(topicParts[2]);
233   - reportReqDTO.setDeviceName(topicParts[3]);
234   -
235   - // 只使用标准 JSON格式处理属性数据
236   - JSONObject params = jsonObject.getJSONObject("params");
237   - if (params == null) {
238   - log.warn("[buildPropertyReportDTO][消息格式不正确,缺少params字段][jsonObject: {}]", jsonObject);
239   - params = new JSONObject();
240   - }
241   -
242   - // 将标准格式的params转换为平台需要的properties格式
243   - Map<String, Object> properties = new HashMap<>();
244   - for (Map.Entry<String, Object> entry : params.entrySet()) {
245   - String key = entry.getKey();
246   - Object valueObj = entry.getValue();
247   -
248   - // 如果是复杂结构(包含value和time)
249   - if (valueObj instanceof JSONObject valueJson) {
250   - properties.put(key, valueJson.getOrDefault("value", valueObj));
251   - } else {
252   - properties.put(key, valueObj);
253   - }
254   - }
255   - reportReqDTO.setProperties(properties);
256   -
257   - return reportReqDTO;
258   - }
259   -
260   - /**
261   - * 构建设备事件上报请求对象
262   - *
263   - * @param jsonObject 消息内容
264   - * @param topicParts 主题部分
265   - * @return 设备事件上报请求对象
266   - */
267   - private IotDeviceEventReportReqDTO buildEventReportDTO(JSONObject jsonObject, String[] topicParts) {
268   - IotDeviceEventReportReqDTO reportReqDTO = new IotDeviceEventReportReqDTO();
269   - reportReqDTO.setRequestId(jsonObject.getStr("id"));
270   - reportReqDTO.setProcessId(IotNetComponentCommonUtils.getProcessId());
271   - reportReqDTO.setReportTime(LocalDateTime.now());
272   - reportReqDTO.setProductKey(topicParts[2]);
273   - reportReqDTO.setDeviceName(topicParts[3]);
274   - reportReqDTO.setIdentifier(topicParts[6]);
275   -
276   - // 只使用标准JSON格式处理事件参数
277   - JSONObject params = jsonObject.getJSONObject("params");
278   - if (params == null) {
279   - log.warn("[buildEventReportDTO][消息格式不正确,缺少params字段][jsonObject: {}]", jsonObject);
280   - params = new JSONObject();
281   - }
282   - reportReqDTO.setParams(params);
283   -
284   - return reportReqDTO;
285   - }
286   -
287   -}
288 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/java/cn/iocoder/yudao/module/iot/net/component/emqx/upstream/router/IotDeviceWebhookVertxHandler.java deleted
1   -package cn.iocoder.yudao.module.iot.net.component.emqx.upstream.router;
2   -
3   -import cn.hutool.core.util.StrUtil;
4   -import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5   -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
6   -import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO;
7   -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
8   -import cn.iocoder.yudao.module.iot.net.component.core.util.IotNetComponentCommonUtils;
9   -import io.vertx.core.Handler;
10   -import io.vertx.core.json.JsonObject;
11   -import io.vertx.ext.web.RoutingContext;
12   -import lombok.RequiredArgsConstructor;
13   -import lombok.extern.slf4j.Slf4j;
14   -
15   -import java.time.LocalDateTime;
16   -import java.util.Collections;
17   -
18   -/**
19   - * IoT EMQX Webhook 事件处理的 Vert.x Handler
20   - * <p>
21   - * 参考:<a href="https://docs.emqx.com/zh/emqx/latest/data-integration/webhook.html">EMQX Webhook</a>
22   - * <p>
23   - * 注意:该处理器需要返回特定格式:{"result": "success"} 或 {"result": "error"},
24   - * 以符合 EMQX Webhook 插件的要求,因此不使用 IotStandardResponse 实体类。
25   - *
26   - * @author haohao
27   - */
28   -@RequiredArgsConstructor
29   -@Slf4j
30   -public class IotDeviceWebhookVertxHandler implements Handler<RoutingContext> {
31   -
32   - public static final String PATH = "/mqtt/webhook";
33   -
34   - private final IotDeviceUpstreamApi deviceUpstreamApi;
35   -
36   - @Override
37   - public void handle(RoutingContext routingContext) {
38   - try {
39   - // 解析请求体
40   - JsonObject json = routingContext.body().asJsonObject();
41   - String event = json.getString("event");
42   - String clientId = json.getString("clientid");
43   - String username = json.getString("username");
44   -
45   - // 处理不同的事件类型
46   - switch (event) {
47   - case "client.connected":
48   - handleClientConnected(clientId, username);
49   - break;
50   - case "client.disconnected":
51   - handleClientDisconnected(clientId, username);
52   - break;
53   - default:
54   - log.info("[handle][未处理的 Webhook 事件] event={}, clientId={}, username={}", event, clientId, username);
55   - break;
56   - }
57   -
58   - // 返回成功响应
59   - // 注意:这里必须返回 {"result": "success"} 格式,以符合 EMQX Webhook 插件的要求
60   - IotNetComponentCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "success"));
61   - } catch (Exception e) {
62   - log.error("[handle][处理 Webhook 事件异常]", e);
63   - // 注意:这里必须返回 {"result": "error"} 格式,以符合 EMQX Webhook 插件的要求
64   - IotNetComponentCommonUtils.writeJsonResponse(routingContext, Collections.singletonMap("result", "error"));
65   - }
66   - }
67   -
68   - /**
69   - * 处理客户端连接事件
70   - *
71   - * @param clientId 客户端ID
72   - * @param username 用户名
73   - */
74   - private void handleClientConnected(String clientId, String username) {
75   - // 解析产品标识和设备名称
76   - if (StrUtil.isEmpty(username) || "undefined".equals(username)) {
77   - log.warn("[handleClientConnected][客户端连接事件,但用户名为空] clientId={}", clientId);
78   - return;
79   - }
80   - String[] parts = parseUsername(username);
81   - if (parts == null) {
82   - return;
83   - }
84   -
85   - // 更新设备状态为在线
86   - IotDeviceStateUpdateReqDTO updateReqDTO = new IotDeviceStateUpdateReqDTO();
87   - updateReqDTO.setProductKey(parts[1]);
88   - updateReqDTO.setDeviceName(parts[0]);
89   - updateReqDTO.setState(IotDeviceStateEnum.ONLINE.getState());
90   - updateReqDTO.setProcessId(IotNetComponentCommonUtils.getProcessId());
91   - updateReqDTO.setReportTime(LocalDateTime.now());
92   - CommonResult<Boolean> result = deviceUpstreamApi.updateDeviceState(updateReqDTO);
93   - if (result.getCode() != 0 || !result.getData()) {
94   - log.error("[handleClientConnected][更新设备状态为在线失败] clientId={}, username={}, code={}, msg={}",
95   - clientId, username, result.getCode(), result.getMsg());
96   - } else {
97   - log.info("[handleClientConnected][更新设备状态为在线成功] clientId={}, username={}", clientId, username);
98   - }
99   - }
100   -
101   - /**
102   - * 处理客户端断开连接事件
103   - *
104   - * @param clientId 客户端ID
105   - * @param username 用户名
106   - */
107   - private void handleClientDisconnected(String clientId, String username) {
108   - // 解析产品标识和设备名称
109   - if (StrUtil.isEmpty(username) || "undefined".equals(username)) {
110   - log.warn("[handleClientDisconnected][客户端断开连接事件,但用户名为空] clientId={}", clientId);
111   - return;
112   - }
113   - String[] parts = parseUsername(username);
114   - if (parts == null) {
115   - return;
116   - }
117   -
118   - // 更新设备状态为离线
119   - IotDeviceStateUpdateReqDTO offlineReqDTO = new IotDeviceStateUpdateReqDTO();
120   - offlineReqDTO.setProductKey(parts[1]);
121   - offlineReqDTO.setDeviceName(parts[0]);
122   - offlineReqDTO.setState(IotDeviceStateEnum.OFFLINE.getState());
123   - offlineReqDTO.setProcessId(IotNetComponentCommonUtils.getProcessId());
124   - offlineReqDTO.setReportTime(LocalDateTime.now());
125   - CommonResult<Boolean> offlineResult = deviceUpstreamApi.updateDeviceState(offlineReqDTO);
126   - if (offlineResult.getCode() != 0 || !offlineResult.getData()) {
127   - log.error("[handleClientDisconnected][更新设备状态为离线失败] clientId={}, username={}, code={}, msg={}",
128   - clientId, username, offlineResult.getCode(), offlineResult.getMsg());
129   - } else {
130   - log.info("[handleClientDisconnected][更新设备状态为离线成功] clientId={}, username={}", clientId, username);
131   - }
132   - }
133   -
134   - /**
135   - * 解析用户名,格式为 deviceName&productKey
136   - *
137   - * @param username 用户名
138   - * @return 解析结果,[0] 为 deviceName,[1] 为 productKey,解析失败返回 null
139   - */
140   - private String[] parseUsername(String username) {
141   - if (StrUtil.isEmpty(username)) {
142   - return null;
143   - }
144   - String[] parts = username.split("&");
145   - if (parts.length != 2) {
146   - log.warn("[parseUsername][用户名格式({})不正确,无法解析产品标识和设备名称]", username);
147   - return null;
148   - }
149   - return parts;
150   - }
151   -
152   -}
153 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted
1   -cn.iocoder.yudao.module.iot.net.component.emqx.config.IotNetComponentEmqxAutoConfiguration
2 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-emqx/src/main/resources/application.yml deleted
1   -# EMQX组件默认配置
2   -yudao:
3   - iot:
4   - component:
5   - # 核心组件配置
6   - core:
7   - plugin-key: emqx # 插件的唯一标识
8   - # EMQX组件配置
9   -# emqx:
10   -# enabled: true # 启用EMQX组件
11   -# mqtt-host: 127.0.0.1 # MQTT服务器主机地址
12   -# mqtt-port: 1883 # MQTT服务器端口
13   -# mqtt-username: yudao # MQTT服务器用户名
14   -# mqtt-password: 123456 # MQTT服务器密码
15   -# mqtt-ssl: false # 是否启用SSL
16   -# mqtt-topics: # 订阅的主题列表
17   -# - "/sys/#"
18   -# auth-port: 8101 # 认证端口
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/pom.xml deleted
1   -<?xml version="1.0" encoding="UTF-8"?>
2   -<project xmlns="http://maven.apache.org/POM/4.0.0"
3   - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4   - <modelVersion>4.0.0</modelVersion>
5   - <parent>
6   - <groupId>cn.iocoder.boot</groupId>
7   - <artifactId>yudao-module-iot-net-components</artifactId>
8   - <version>${revision}</version>
9   - </parent>
10   -
11   - <artifactId>yudao-module-iot-net-component-server</artifactId>
12   - <packaging>jar</packaging>
13   -
14   - <name>${project.artifactId}</name>
15   - <description>
16   - IoT 网络组件的独立运行服务,用于聚合和启动多个网络组件实例。
17   - </description>
18   -
19   - <dependencies>
20   - <!-- Web 相关 -->
21   - <dependency>
22   - <groupId>org.springframework.boot</groupId>
23   - <artifactId>spring-boot-starter-web</artifactId>
24   - </dependency>
25   -
26   - <!-- Net Component EMQX -->
27   - <dependency>
28   - <groupId>cn.iocoder.boot</groupId>
29   - <artifactId>yudao-module-iot-net-component-emqx</artifactId>
30   - <version>${revision}</version>
31   - </dependency>
32   -
33   -
34   -
35   - </dependencies>
36   -
37   -</project>
38 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/src/main/java/cn/iocoder/yudao/module/iot/net/component/server/config/IotNetComponentServerConfiguration.java deleted
1   -package cn.iocoder.yudao.module.iot.net.component.server.config;
2   -
3   -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
4   -import cn.iocoder.yudao.module.iot.net.component.server.upstream.IotComponentUpstreamClient;
5   -import org.springframework.boot.context.properties.EnableConfigurationProperties;
6   -import org.springframework.boot.web.client.RestTemplateBuilder;
7   -import org.springframework.context.annotation.Bean;
8   -import org.springframework.context.annotation.Configuration;
9   -import org.springframework.context.annotation.Primary;
10   -import org.springframework.scheduling.annotation.EnableScheduling;
11   -import org.springframework.web.client.RestTemplate;
12   -
13   -/**
14   - * IoT 网络组件服务器配置类
15   - *
16   - * @author haohao
17   - */
18   -@Configuration
19   -@EnableConfigurationProperties(IotNetComponentServerProperties.class)
20   -@EnableScheduling
21   -public class IotNetComponentServerConfiguration {
22   -
23   - /**
24   - * 配置 RestTemplate
25   - *
26   - * @param properties 配置
27   - * @return RestTemplate
28   - */
29   - @Bean
30   - // TODO @haohao:貌似要独立一个 restTemplate 的名字?不然容易冲突;
31   - public RestTemplate restTemplate(IotNetComponentServerProperties properties) {
32   - return new RestTemplateBuilder()
33   - .connectTimeout(properties.getUpstreamConnectTimeout())
34   - .readTimeout(properties.getUpstreamReadTimeout())
35   - .build();
36   - }
37   -
38   - /**
39   - * 配置设备上行客户端
40   - *
41   - * @param properties 配置
42   - * @param restTemplate RestTemplate
43   - * @return 上行客户端
44   - */
45   - @Bean
46   - @Primary
47   - public IotDeviceUpstreamApi deviceUpstreamApi(IotNetComponentServerProperties properties,
48   - RestTemplate restTemplate) {
49   - return new IotComponentUpstreamClient(properties, restTemplate);
50   - }
51   -
52   -}
53 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-net-components/yudao-module-iot-net-component-server/src/main/java/cn/iocoder/yudao/module/iot/net/component/server/config/IotNetComponentServerProperties.java deleted
1   -package cn.iocoder.yudao.module.iot.net.component.server.config;
2   -
3   -import lombok.Data;
4   -import org.springframework.boot.context.properties.ConfigurationProperties;
5   -import org.springframework.validation.annotation.Validated;
6   -
7   -import java.time.Duration;
8   -
9   -/**
10   - * IoT 网络组件服务配置属性
11   - *
12   - * @author haohao
13   - */
14   -@ConfigurationProperties(prefix = "yudao.iot.component.server")
15   -@Validated
16   -@Data
17   -public class IotNetComponentServerProperties {
18   -
19   - /**
20   - * 上行 URL,用于向主应用程序上报数据
21   - */
22   - private String upstreamUrl = "http://127.0.0.1:48080";
23   -
24   - /**
25   - * 上行连接超时时间
26   - */
27   - private Duration upstreamConnectTimeout = Duration.ofSeconds(30);
28   -
29   - /**
30   - * 上行读取超时时间
31   - */
32   - private Duration upstreamReadTimeout = Duration.ofSeconds(30);
33   -
34   -}
35 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/README.md deleted
1   -# IoT 协议模块 (yudao-module-iot-protocol)
2   -
3   -## 概述
4   -
5   -本模块是物联网协议处理的核心组件,提供统一的协议解析、转换和消息处理功能。作为 `yudao-module-iot-biz` 和
6   -`yudao-module-iot-gateway-server` 等模块的共享包,实现了协议层面的抽象和统一。
7   -
8   -## 主要功能
9   -
10   -### 1. 协议消息模型
11   -
12   -- **IotMqttMessage**: 基于 MQTT 协议规范的标准消息模型(默认实现)
13   -- **IotStandardResponse**: 统一的响应格式,支持 MQTT 和 HTTP 协议
14   -
15   -### 2. 主题管理
16   -
17   -- **IotTopicConstants**: 主题常量定义
18   -- **IotTopicUtils**: MQTT 主题构建和解析工具
19   -- **IotHttpTopicUtils**: HTTP 主题构建和解析工具
20   -- **IotTopicParser**: 高级主题解析器,支持提取设备信息、消息类型等
21   -
22   -### 3. 协议转换
23   -
24   -- **IotMessageParser**: 消息解析器接口
25   -- **IotMqttMessageParser**: MQTT 协议解析器实现(默认)
26   -- **IotHttpMessageParser**: HTTP 协议解析器实现
27   -- **IotProtocolConverter**: 协议转换器接口
28   -- **DefaultIotProtocolConverter**: 默认协议转换器实现
29   -
30   -### 4. 枚举定义
31   -
32   -- **IotProtocolTypeEnum**: 协议类型枚举
33   -- **IotMessageTypeEnum**: 消息类型枚举
34   -- **IotMessageDirectionEnum**: 消息方向枚举
35   -
36   -## 使用示例
37   -
38   -### 1. 构建主题
39   -
40   -#### MQTT 主题
41   -
42   -```java
43   -// 构建设备属性设置主题
44   -String topic = IotTopicUtils.buildPropertySetTopic("productKey", "deviceName");
45   -// 结果: /sys/productKey/deviceName/thing/service/property/set
46   -
47   -// 构建事件上报主题
48   -String eventTopic = IotTopicUtils.buildEventPostTopic("productKey", "deviceName", "temperature");
49   -// 结果: /sys/productKey/deviceName/thing/event/temperature/post
50   -
51   -// 获取响应主题
52   -String replyTopic = IotTopicUtils.getReplyTopic(topic);
53   -// 结果: /sys/productKey/deviceName/thing/service/property/set_reply
54   -```
55   -
56   -#### HTTP 主题
57   -
58   -```java
59   -// 构建属性设置路径
60   -String propSetPath = IotHttpTopicUtils.buildPropertySetPath("productKey", "deviceName");
61   -// 结果: /topic/sys/productKey/deviceName/thing/service/property/set
62   -
63   -// 构建属性获取路径
64   -String propGetPath = IotHttpTopicUtils.buildPropertyGetPath("productKey", "deviceName");
65   -// 结果: /topic/sys/productKey/deviceName/thing/service/property/get
66   -
67   -// 构建事件上报路径
68   -String eventPath = IotHttpTopicUtils.buildEventPostPath("productKey", "deviceName", "alarm");
69   -// 结果: /topic/sys/productKey/deviceName/thing/event/alarm/post
70   -
71   -// 构建自定义主题路径
72   -String customPath = IotHttpTopicUtils.buildCustomTopicPath("productKey", "deviceName", "user/get");
73   -// 结果: /topic/productKey/deviceName/user/get
74   -```
75   -
76   -### 2. 解析主题
77   -
78   -```java
79   -// 解析 MQTT 主题信息
80   -IotTopicParser.TopicInfo info = IotTopicParser.parse("/sys/pk/dn/thing/service/property/set");
81   -System.out.
82   -
83   -println("产品Key: "+info.getProductKey()); // pk
84   - System.out.
85   -
86   -println("设备名称: "+info.getDeviceName()); // dn
87   - System.out.
88   -
89   -println("消息类型: "+info.getMessageType()); // PROPERTY_SET
90   - System.out.
91   -
92   -println("消息方向: "+info.getDirection()); // DOWNSTREAM
93   -
94   -// 解析 HTTP 主题信息
95   -String httpPath = "/topic/sys/pk/dn/thing/service/property/set";
96   -String actualTopic = IotHttpTopicUtils.extractActualTopic(httpPath); // /sys/pk/dn/thing/service/property/set
97   -String productKey = IotHttpTopicUtils.parseProductKeyFromTopic(actualTopic); // pk
98   -String deviceName = IotHttpTopicUtils.parseDeviceNameFromTopic(actualTopic); // dn
99   -```
100   -
101   -### 3. 创建 MQTT 消息
102   -
103   -```java
104   -// 创建属性设置消息
105   -Map<String, Object> properties = new HashMap<>();
106   -properties.
107   -
108   -put("temperature",25.5);
109   -
110   -IotMqttMessage message = IotMqttMessage.createPropertySetMessage("123456", properties);
111   -
112   -// 转换为 JSON 字符串
113   -String json = message.toJsonString();
114   -```
115   -
116   -### 4. HTTP 协议消息处理
117   -
118   -#### HTTP 消息格式
119   -
120   -```json
121   -{
122   - "deviceKey": "productKey/deviceName",
123   - "messageId": "123456",
124   - "action": "property.set",
125   - "version": "1.0",
126   - "data": {
127   - "temperature": 25.5,
128   - "humidity": 60.0
129   - }
130   -}
131   -```
132   -
133   -#### 使用 HTTP 协议解析器
134   -
135   -```java
136   -// 创建 HTTP 协议解析器
137   -IotHttpMessageParser httpParser = new IotHttpMessageParser();
138   -
139   -// 解析 HTTP 消息
140   -String topic = "/topic/sys/productKey/deviceName/thing/service/property/set";
141   -byte[] payload = httpMessage.getBytes(StandardCharsets.UTF_8);
142   -IotMqttMessage message = httpParser.parse(topic, payload);
143   -
144   -// 格式化 HTTP 响应
145   -IotStandardResponse response = IotStandardResponse.success("123456", "property.set", data);
146   -byte[] responseBytes = httpParser.formatResponse(response);
147   -```
148   -
149   -### 5. 使用协议转换器
150   -
151   -```java
152   -
153   -@Autowired
154   -private IotProtocolConverter protocolConverter;
155   -
156   -// 转换 MQTT 消息(推荐使用)
157   -IotMqttMessage mqttMessage = protocolConverter.convertToStandardMessage(mqttTopic, mqttPayload, "mqtt");
158   -
159   -// 转换 HTTP 消息
160   -IotMqttMessage httpMessage = protocolConverter.convertToStandardMessage(httpTopic, httpPayload, "http");
161   -
162   -// 创建响应
163   -IotStandardResponse response = IotStandardResponse.success("123456", "property.set", data);
164   -byte[] responseBytes = protocolConverter.convertFromStandardResponse(response, "mqtt");
165   -```
166   -
167   -### 6. 自定义协议解析器
168   -
169   -```java
170   -
171   -@Component
172   -public class CustomMessageParser implements IotMessageParser {
173   -
174   - @Override
175   - public IotMqttMessage parse(String topic, byte[] payload) {
176   - // 实现自定义协议解析逻辑
177   - return null;
178   - }
179   -
180   - @Override
181   - public byte[] formatResponse(IotStandardResponse response) {
182   - // 实现自定义响应格式化逻辑
183   - return new byte[0];
184   - }
185   -
186   - @Override
187   - public boolean canHandle(String topic) {
188   - // 判断是否能处理该主题
189   - return topic.startsWith("/custom/");
190   - }
191   -}
192   -
193   -// 注册到协议转换器
194   -@Autowired
195   -private DefaultIotProtocolConverter converter;
196   -
197   -@PostConstruct
198   -public void init() {
199   - converter.registerParser("custom", new CustomMessageParser());
200   -}
201   -```
202   -
203   -## 支持的协议类型
204   -
205   -- **MQTT**: 标准 MQTT 协议,支持设备属性、事件、服务调用(默认协议)
206   -- **HTTP**: HTTP 协议,支持设备通过 HTTP API 进行通信
207   -- **MQTT_RAW**: MQTT 原始协议
208   -- **TCP**: TCP 协议
209   -- **UDP**: UDP 协议
210   -- **CUSTOM**: 自定义协议
211   -
212   -## 协议对比
213   -
214   -| 协议类型 | 传输方式 | 消息格式 | 主题格式 | 适用场景 |
215   -|----------|------|------|----------------------------------------------------------------------------------------------------------------------------|---------------|
216   -| MQTT | MQTT | JSON | `/sys/{productKey}/{deviceName}/...`<br/>`/mqtt/{productKey}/{deviceName}/...`<br/>`/device/{productKey}/{deviceName}/...` | 实时性要求高的设备(推荐) |
217   -| HTTP | HTTP | JSON | `/topic/sys/{productKey}/{deviceName}/...`<br/>`/topic/{productKey}/{deviceName}/...` | 间歇性通信的设备 |
218   -| MQTT_RAW | MQTT | 原始 | 自定义格式 | 特殊协议设备 |
219   -
220   -## 模块依赖
221   -
222   -本模块是一个基础模块,依赖项最小化:
223   -
224   -- `yudao-common`: 基础工具类
225   -- `hutool-all`: 工具库
226   -- `lombok`: 简化代码
227   -- `spring-boot-starter`: Spring Boot 基础支持
228   -
229   -## 扩展点
230   -
231   -### 1. 自定义消息解析器
232   -
233   -实现 `IotMessageParser` 接口,支持新的协议格式。
234   -
235   -### 2. 自定义协议转换器
236   -
237   -实现 `IotProtocolConverter` 接口,提供更复杂的转换逻辑。
238   -
239   -### 3. 自定义主题格式
240   -
241   -扩展 `IotTopicParser` 的 `parseCustomTopic` 方法,支持自定义主题格式。
242   -
243   -## 注意事项
244   -
245   -1. 本模块设计为无状态的工具模块,避免引入有状态的组件
246   -2. 所有的工具类都采用静态方法,便于直接调用
247   -3. 异常处理采用返回 null 的方式,调用方需要做好空值检查
248   -4. 日志级别建议设置为 INFO 或 WARN,避免过多的 DEBUG 日志
249   -5. HTTP 协议解析器使用设备标识 `deviceKey`(格式:`productKey/deviceName`)来标识设备
250   -
251   -## 版本更新
252   -
253   -- v1.0.0: 基础功能实现,支持 MQTT 协议和 HTTP 协议支持
254   -- 后续版本将支持更多协议类型和高级功能
255 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/pom.xml deleted
1   -<?xml version="1.0" encoding="UTF-8"?>
2   -<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3   - xmlns="http://maven.apache.org/POM/4.0.0"
4   - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5   - <parent>
6   - <artifactId>yudao-module-iot</artifactId>
7   - <groupId>cn.iocoder.boot</groupId>
8   - <version>${revision}</version>
9   - </parent>
10   - <modelVersion>4.0.0</modelVersion>
11   -
12   - <artifactId>yudao-module-iot-protocol</artifactId>
13   - <packaging>jar</packaging>
14   -
15   - <name>${project.artifactId}</name>
16   - <description>
17   - 物联网协议模块,提供 topic 解析、协议转换等功能
18   - 作为 iot-biz 和 iot-gateway 的共享包
19   - </description>
20   -
21   - <dependencies>
22   - <!-- 项目内部依赖 -->
23   - <dependency>
24   - <groupId>cn.iocoder.boot</groupId>
25   - <artifactId>yudao-common</artifactId>
26   - </dependency>
27   -
28   - <!-- Web 相关 -->
29   - <dependency>
30   - <groupId>cn.iocoder.boot</groupId>
31   - <artifactId>yudao-spring-boot-starter-web</artifactId>
32   - <scope>provided</scope> <!-- 设为 provided,只有 biz 需要使用 -->
33   - </dependency>
34   -
35   - <!-- 工具类相关 -->
36   - <dependency>
37   - <groupId>org.projectlombok</groupId>
38   - <artifactId>lombok</artifactId>
39   - </dependency>
40   -
41   - <dependency>
42   - <groupId>cn.hutool</groupId>
43   - <artifactId>hutool-all</artifactId>
44   - </dependency>
45   -
46   - <!-- IoT 相关依赖 -->
47   - <dependency>
48   - <groupId>io.vertx</groupId>
49   - <artifactId>vertx-core</artifactId>
50   - <scope>provided</scope>
51   - </dependency>
52   - <dependency>
53   - <groupId>io.vertx</groupId>
54   - <artifactId>vertx-web</artifactId>
55   - <scope>provided</scope>
56   - </dependency>
57   -
58   - <!-- 测试相关 -->
59   - <dependency>
60   - <groupId>org.junit.jupiter</groupId>
61   - <artifactId>junit-jupiter</artifactId>
62   - <scope>test</scope>
63   - </dependency>
64   - <dependency>
65   - <groupId>org.springframework.boot</groupId>
66   - <artifactId>spring-boot-starter-test</artifactId>
67   - <scope>test</scope>
68   - </dependency>
69   - </dependencies>
70   -
71   -</project>
72 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/config/IotProtocolAutoConfiguration.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.config;
2   -
3   -import cn.iocoder.yudao.module.iot.protocol.convert.IotProtocolConverter;
4   -import cn.iocoder.yudao.module.iot.protocol.convert.impl.DefaultIotProtocolConverter;
5   -import cn.iocoder.yudao.module.iot.protocol.enums.IotProtocolTypeEnum;
6   -import cn.iocoder.yudao.module.iot.protocol.message.IotMessageParser;
7   -import cn.iocoder.yudao.module.iot.protocol.message.impl.IotHttpMessageParser;
8   -import cn.iocoder.yudao.module.iot.protocol.message.impl.IotMqttMessageParser;
9   -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
10   -import org.springframework.context.annotation.Bean;
11   -import org.springframework.context.annotation.Configuration;
12   -
13   -/**
14   - * IoT 协议模块自动配置类
15   - *
16   - * @author haohao
17   - */
18   -@Configuration(proxyBeanMethods = false)
19   -public class IotProtocolAutoConfiguration {
20   -
21   - /**
22   - * Bean 名称常量
23   - */
24   - public static final String IOT_MQTT_MESSAGE_PARSER_BEAN_NAME = "iotMqttMessageParser";
25   - public static final String IOT_HTTP_MESSAGE_PARSER_BEAN_NAME = "iotHttpMessageParser";
26   -
27   - /**
28   - * 注册 MQTT 协议消息解析器
29   - *
30   - * @return MQTT 协议消息解析器
31   - */
32   - @Bean
33   - @ConditionalOnMissingBean(name = IOT_MQTT_MESSAGE_PARSER_BEAN_NAME)
34   - public IotMessageParser iotMqttMessageParser() {
35   - return new IotMqttMessageParser();
36   - }
37   -
38   -
39   - /**
40   - * 注册 HTTP 协议消息解析器
41   - *
42   - * @return HTTP 协议消息解析器
43   - */
44   - @Bean
45   - @ConditionalOnMissingBean(name = IOT_HTTP_MESSAGE_PARSER_BEAN_NAME)
46   - public IotMessageParser iotHttpMessageParser() {
47   - return new IotHttpMessageParser();
48   - }
49   -
50   - /**
51   - * 注册默认协议转换器
52   - * <p>
53   - * 如果用户没有自定义协议转换器,则使用默认实现
54   - * 默认会注册 MQTT 和 HTTP 协议解析器
55   - *
56   - * @param iotMqttMessageParser MQTT 协议解析器
57   - * @param iotHttpMessageParser HTTP 协议解析器
58   - * @return 默认协议转换器
59   - */
60   - @Bean
61   - @ConditionalOnMissingBean
62   - public IotProtocolConverter iotProtocolConverter(IotMessageParser iotMqttMessageParser,
63   - IotMessageParser iotHttpMessageParser) {
64   - DefaultIotProtocolConverter converter = new DefaultIotProtocolConverter();
65   -
66   - // 注册 MQTT 协议解析器(默认实现)
67   - converter.registerParser(IotProtocolTypeEnum.MQTT.getCode(), iotMqttMessageParser);
68   -
69   - // 注册 HTTP 协议解析器
70   - converter.registerParser(IotProtocolTypeEnum.HTTP.getCode(), iotHttpMessageParser);
71   -
72   - return converter;
73   - }
74   -}
75 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/constants/IotHttpConstants.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.constants;
2   -
3   -/**
4   - * IoT HTTP 协议常量类
5   - * <p>
6   - * 用于统一管理 HTTP 协议中的常量,包括路径、字段名、默认值等
7   - *
8   - * @author haohao
9   - */
10   -public class IotHttpConstants {
11   -
12   - /**
13   - * 路径常量
14   - */
15   - public static class Path {
16   - /**
17   - * 认证路径
18   - */
19   - public static final String AUTH = "/auth";
20   -
21   - /**
22   - * 主题路径前缀
23   - */
24   - public static final String TOPIC_PREFIX = "/topic";
25   - }
26   -
27   - /**
28   - * 认证字段常量
29   - */
30   - public static class AuthField {
31   - /**
32   - * 产品Key
33   - */
34   - public static final String PRODUCT_KEY = "productKey";
35   -
36   - /**
37   - * 设备名称
38   - */
39   - public static final String DEVICE_NAME = "deviceName";
40   -
41   - /**
42   - * 客户端ID
43   - */
44   - public static final String CLIENT_ID = "clientId";
45   -
46   - /**
47   - * 时间戳
48   - */
49   - public static final String TIMESTAMP = "timestamp";
50   -
51   - /**
52   - * 签名
53   - */
54   - public static final String SIGN = "sign";
55   -
56   - /**
57   - * 签名方法
58   - */
59   - public static final String SIGN_METHOD = "signmethod";
60   -
61   - /**
62   - * 版本
63   - */
64   - public static final String VERSION = "version";
65   - }
66   -
67   - /**
68   - * 消息字段常量
69   - */
70   - public static class MessageField {
71   - /**
72   - * 消息ID
73   - */
74   - public static final String ID = "id";
75   -
76   - /**
77   - * 方法名
78   - */
79   - public static final String METHOD = "method";
80   -
81   - /**
82   - * 版本
83   - */
84   - public static final String VERSION = "version";
85   -
86   - /**
87   - * 参数
88   - */
89   - public static final String PARAMS = "params";
90   -
91   - /**
92   - * 数据
93   - */
94   - public static final String DATA = "data";
95   - }
96   -
97   - /**
98   - * 响应字段常量
99   - */
100   - public static class ResponseField {
101   - /**
102   - * 状态码
103   - */
104   - public static final String CODE = "code";
105   -
106   - /**
107   - * 消息
108   - */
109   - public static final String MESSAGE = "message";
110   -
111   - /**
112   - * 信息
113   - */
114   - public static final String INFO = "info";
115   -
116   - /**
117   - * 令牌
118   - */
119   - public static final String TOKEN = "token";
120   -
121   - /**
122   - * 消息ID
123   - */
124   - public static final String MESSAGE_ID = "messageId";
125   - }
126   -
127   - /**
128   - * 默认值常量
129   - */
130   - public static class DefaultValue {
131   - /**
132   - * 默认签名方法
133   - */
134   - public static final String SIGN_METHOD = "hmacmd5";
135   -
136   - /**
137   - * 默认版本
138   - */
139   - public static final String VERSION = "default";
140   -
141   - /**
142   - * 默认消息版本
143   - */
144   - public static final String MESSAGE_VERSION = "1.0";
145   -
146   - /**
147   - * 未知方法名
148   - */
149   - public static final String UNKNOWN_METHOD = "unknown";
150   - }
151   -
152   - /**
153   - * 方法名常量
154   - */
155   - public static class Method {
156   - /**
157   - * 设备认证
158   - */
159   - public static final String DEVICE_AUTH = "device.auth";
160   -
161   - /**
162   - * 自定义消息
163   - */
164   - public static final String CUSTOM_MESSAGE = "custom.message";
165   - }
166   -}
167 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/constants/IotLogConstants.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.constants;
2   -
3   -/**
4   - * IoT 协议日志消息常量类
5   - * <p>
6   - * 用于统一管理协议模块中的日志消息常量
7   - *
8   - * @author haohao
9   - */
10   -public class IotLogConstants {
11   -
12   - /**
13   - * HTTP 协议日志消息
14   - */
15   - public static class Http {
16   - /**
17   - * 收到空消息内容
18   - */
19   - public static final String RECEIVED_EMPTY_MESSAGE = "[HTTP] 收到空消息内容, topic={}";
20   -
21   - /**
22   - * 不支持的路径格式
23   - */
24   - public static final String UNSUPPORTED_PATH_FORMAT = "[HTTP] 不支持的路径格式, topic={}";
25   -
26   - /**
27   - * 解析消息失败
28   - */
29   - public static final String PARSE_MESSAGE_FAILED = "[HTTP] 解析消息失败, topic={}";
30   -
31   - /**
32   - * 认证消息非JSON格式
33   - */
34   - public static final String AUTH_MESSAGE_NOT_JSON = "[HTTP] 认证消息非JSON格式, message={}";
35   -
36   - /**
37   - * 认证消息缺少必需字段
38   - */
39   - public static final String AUTH_MESSAGE_MISSING_REQUIRED_FIELDS = "[HTTP] 认证消息缺少必需字段, message={}";
40   -
41   - /**
42   - * 格式化响应失败
43   - */
44   - public static final String FORMAT_RESPONSE_FAILED = "[HTTP] 格式化响应失败";
45   - }
46   -
47   - /**
48   - * 协议转换器日志消息
49   - */
50   - public static class Converter {
51   - /**
52   - * 注册协议解析器
53   - */
54   - public static final String REGISTER_PARSER = "[协议转换器] 注册协议解析器: protocol={}, parser={}";
55   -
56   - /**
57   - * 移除协议解析器
58   - */
59   - public static final String REMOVE_PARSER = "[协议转换器] 移除协议解析器: protocol={}";
60   -
61   - /**
62   - * 不支持的协议类型
63   - */
64   - public static final String UNSUPPORTED_PROTOCOL = "[协议转换器] 不支持的协议类型: protocol={}";
65   -
66   - /**
67   - * 转换消息失败
68   - */
69   - public static final String CONVERT_MESSAGE_FAILED = "[协议转换器] 转换消息失败: protocol={}, topic={}";
70   -
71   - /**
72   - * 格式化响应失败
73   - */
74   - public static final String FORMAT_RESPONSE_FAILED = "[协议转换器] 格式化响应失败: protocol={}";
75   -
76   - /**
77   - * 自动选择协议
78   - */
79   - public static final String AUTO_SELECT_PROTOCOL = "[协议转换器] 自动选择协议: protocol={}, topic={}";
80   -
81   - /**
82   - * 协议解析失败,尝试下一个
83   - */
84   - public static final String PROTOCOL_PARSE_FAILED_TRY_NEXT = "[协议转换器] 协议解析失败,尝试下一个: protocol={}, topic={}";
85   -
86   - /**
87   - * 无法自动识别协议
88   - */
89   - public static final String CANNOT_AUTO_RECOGNIZE_PROTOCOL = "[协议转换器] 无法自动识别协议: topic={}";
90   - }
91   -}
92 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/constants/IotTopicConstants.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.constants;
2   -
3   -/**
4   - * IoT 设备主题常量类
5   - * <p>
6   - * 用于统一管理 MQTT 协议中的主题常量,基于 Alink 协议规范
7   - *
8   - * @author haohao
9   - */
10   -public class IotTopicConstants {
11   -
12   - /**
13   - * 系统主题前缀
14   - */
15   - public static final String SYS_TOPIC_PREFIX = "/sys/";
16   -
17   - /**
18   - * 服务调用主题前缀
19   - */
20   - public static final String SERVICE_TOPIC_PREFIX = "/thing/service/";
21   -
22   - /**
23   - * 设备属性设置主题
24   - * 请求Topic:/sys/${productKey}/${deviceName}/thing/service/property/set
25   - * 响应Topic:/sys/${productKey}/${deviceName}/thing/service/property/set_reply
26   - */
27   - public static final String PROPERTY_SET_TOPIC = "/thing/service/property/set";
28   -
29   - /**
30   - * 设备属性获取主题
31   - * 请求Topic:/sys/${productKey}/${deviceName}/thing/service/property/get
32   - * 响应Topic:/sys/${productKey}/${deviceName}/thing/service/property/get_reply
33   - */
34   - public static final String PROPERTY_GET_TOPIC = "/thing/service/property/get";
35   -
36   - /**
37   - * 设备配置设置主题
38   - * 请求Topic:/sys/${productKey}/${deviceName}/thing/service/config/set
39   - * 响应Topic:/sys/${productKey}/${deviceName}/thing/service/config/set_reply
40   - */
41   - public static final String CONFIG_SET_TOPIC = "/thing/service/config/set";
42   -
43   - /**
44   - * 设备OTA升级主题
45   - * 请求Topic:/sys/${productKey}/${deviceName}/thing/service/ota/upgrade
46   - * 响应Topic:/sys/${productKey}/${deviceName}/thing/service/ota/upgrade_reply
47   - */
48   - public static final String OTA_UPGRADE_TOPIC = "/thing/service/ota/upgrade";
49   -
50   - /**
51   - * 设备属性上报主题
52   - * 请求Topic:/sys/${productKey}/${deviceName}/thing/event/property/post
53   - * 响应Topic:/sys/${productKey}/${deviceName}/thing/event/property/post_reply
54   - */
55   - public static final String PROPERTY_POST_TOPIC = "/thing/event/property/post";
56   -
57   - /**
58   - * 设备事件上报主题前缀
59   - */
60   - public static final String EVENT_POST_TOPIC_PREFIX = "/thing/event/";
61   -
62   - /**
63   - * 设备事件上报主题后缀
64   - */
65   - public static final String EVENT_POST_TOPIC_SUFFIX = "/post";
66   -
67   - /**
68   - * 响应主题后缀
69   - */
70   - public static final String REPLY_SUFFIX = "_reply";
71   -
72   - /**
73   - * 方法名前缀常量
74   - */
75   - public static class MethodPrefix {
76   - /**
77   - * 物模型服务前缀
78   - */
79   - public static final String THING_SERVICE = "thing.service.";
80   -
81   - /**
82   - * 物模型事件前缀
83   - */
84   - public static final String THING_EVENT = "thing.event.";
85   - }
86   -
87   - /**
88   - * 完整方法名常量
89   - */
90   - public static class Method {
91   - /**
92   - * 属性设置方法
93   - */
94   - public static final String PROPERTY_SET = "thing.service.property.set";
95   -
96   - /**
97   - * 属性获取方法
98   - */
99   - public static final String PROPERTY_GET = "thing.service.property.get";
100   -
101   - /**
102   - * 属性上报方法
103   - */
104   - public static final String PROPERTY_POST = "thing.event.property.post";
105   -
106   - /**
107   - * 配置设置方法
108   - */
109   - public static final String CONFIG_SET = "thing.service.config.set";
110   -
111   - /**
112   - * OTA升级方法
113   - */
114   - public static final String OTA_UPGRADE = "thing.service.ota.upgrade";
115   -
116   - /**
117   - * 设备上线方法
118   - */
119   - public static final String DEVICE_ONLINE = "device.online";
120   -
121   - /**
122   - * 设备下线方法
123   - */
124   - public static final String DEVICE_OFFLINE = "device.offline";
125   -
126   - /**
127   - * 心跳方法
128   - */
129   - public static final String HEARTBEAT = "heartbeat";
130   - }
131   -
132   - /**
133   - * 主题关键字常量
134   - */
135   - public static class Keyword {
136   - /**
137   - * 事件关键字
138   - */
139   - public static final String EVENT = "event";
140   -
141   - /**
142   - * 服务关键字
143   - */
144   - public static final String SERVICE = "service";
145   -
146   - /**
147   - * 属性关键字
148   - */
149   - public static final String PROPERTY = "property";
150   -
151   - /**
152   - * 上报关键字
153   - */
154   - public static final String POST = "post";
155   - }
156   -
157   -}
158 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/convert/IotProtocolConverter.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.convert;
2   -
3   -import cn.iocoder.yudao.module.iot.protocol.message.IotMqttMessage;
4   -import cn.iocoder.yudao.module.iot.protocol.message.IotStandardResponse;
5   -
6   -/**
7   - * IoT 协议转换器接口
8   - * <p>
9   - * 用于在不同协议之间进行转换
10   - *
11   - * @author haohao
12   - */
13   -public interface IotProtocolConverter {
14   -
15   - /**
16   - * 将字节数组转换为标准消息
17   - *
18   - * @param topic 主题
19   - * @param payload 消息负载
20   - * @param protocol 协议类型
21   - * @return 标准消息对象,转换失败返回 null
22   - */
23   - IotMqttMessage convertToStandardMessage(String topic, byte[] payload, String protocol);
24   -
25   - /**
26   - * 将标准响应转换为字节数组
27   - *
28   - * @param response 标准响应
29   - * @param protocol 协议类型
30   - * @return 字节数组,转换失败返回空数组
31   - */
32   - byte[] convertFromStandardResponse(IotStandardResponse response, String protocol);
33   -
34   - /**
35   - * 检查是否支持指定协议
36   - *
37   - * @param protocol 协议类型
38   - * @return 如果支持返回 true,否则返回 false
39   - */
40   - boolean supportsProtocol(String protocol);
41   -
42   - /**
43   - * 获取支持的协议类型列表
44   - *
45   - * @return 协议类型数组
46   - */
47   - String[] getSupportedProtocols();
48   -}
49 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/convert/impl/DefaultIotProtocolConverter.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.convert.impl;
2   -
3   -import cn.iocoder.yudao.module.iot.protocol.constants.IotLogConstants;
4   -import cn.iocoder.yudao.module.iot.protocol.convert.IotProtocolConverter;
5   -import cn.iocoder.yudao.module.iot.protocol.enums.IotProtocolTypeEnum;
6   -import cn.iocoder.yudao.module.iot.protocol.message.IotMessageParser;
7   -import cn.iocoder.yudao.module.iot.protocol.message.IotMqttMessage;
8   -import cn.iocoder.yudao.module.iot.protocol.message.IotStandardResponse;
9   -import cn.iocoder.yudao.module.iot.protocol.message.impl.IotMqttMessageParser;
10   -import lombok.extern.slf4j.Slf4j;
11   -
12   -import java.util.HashMap;
13   -import java.util.Map;
14   -import java.util.Set;
15   -
16   -/**
17   - * 默认 IoT 协议转换器实现
18   - * <p>
19   - * 支持多种协议的转换,可以通过注册不同的消息解析器来扩展支持的协议
20   - *
21   - * @author haohao
22   - */
23   -@Slf4j
24   -public class DefaultIotProtocolConverter implements IotProtocolConverter {
25   -
26   - /**
27   - * 消息解析器映射
28   - * Key: 协议类型,Value: 消息解析器
29   - */
30   - private final Map<String, IotMessageParser> parsers = new HashMap<>();
31   -
32   - /**
33   - * 构造函数,初始化默认支持的协议
34   - */
35   - public DefaultIotProtocolConverter() {
36   - // 注册 MQTT 协议解析器作为默认实现
37   - IotMqttMessageParser mqttParser = new IotMqttMessageParser();
38   - registerParser(IotProtocolTypeEnum.MQTT.getCode(), mqttParser);
39   - }
40   -
41   - /**
42   - * 注册消息解析器
43   - *
44   - * @param protocol 协议类型
45   - * @param parser 消息解析器
46   - */
47   - public void registerParser(String protocol, IotMessageParser parser) {
48   - parsers.put(protocol, parser);
49   - log.info(IotLogConstants.Converter.REGISTER_PARSER, protocol, parser.getClass().getSimpleName());
50   - }
51   -
52   - /**
53   - * 移除消息解析器
54   - *
55   - * @param protocol 协议类型
56   - */
57   - public void removeParser(String protocol) {
58   - parsers.remove(protocol);
59   - log.info(IotLogConstants.Converter.REMOVE_PARSER, protocol);
60   - }
61   -
62   - @Override
63   - public IotMqttMessage convertToStandardMessage(String topic, byte[] payload, String protocol) {
64   - IotMessageParser parser = parsers.get(protocol);
65   - if (parser == null) {
66   - log.warn(IotLogConstants.Converter.UNSUPPORTED_PROTOCOL, protocol);
67   - return null;
68   - }
69   -
70   - try {
71   - return parser.parse(topic, payload);
72   - } catch (Exception e) {
73   - log.error(IotLogConstants.Converter.CONVERT_MESSAGE_FAILED, protocol, topic, e);
74   - return null;
75   - }
76   - }
77   -
78   - @Override
79   - public byte[] convertFromStandardResponse(IotStandardResponse response, String protocol) {
80   - IotMessageParser parser = parsers.get(protocol);
81   - if (parser == null) {
82   - log.warn(IotLogConstants.Converter.UNSUPPORTED_PROTOCOL, protocol);
83   - return new byte[0];
84   - }
85   -
86   - try {
87   - return parser.formatResponse(response);
88   - } catch (Exception e) {
89   - log.error(IotLogConstants.Converter.FORMAT_RESPONSE_FAILED, protocol, e);
90   - return new byte[0];
91   - }
92   - }
93   -
94   - @Override
95   - public boolean supportsProtocol(String protocol) {
96   - return parsers.containsKey(protocol);
97   - }
98   -
99   - @Override
100   - public String[] getSupportedProtocols() {
101   - Set<String> protocols = parsers.keySet();
102   - return protocols.toArray(new String[0]);
103   - }
104   -
105   - /**
106   - * 根据主题自动选择合适的协议解析器
107   - *
108   - * @param topic 主题
109   - * @param payload 消息负载
110   - * @return 解析后的标准消息,如果无法解析返回 null
111   - */
112   - public IotMqttMessage autoConvert(String topic, byte[] payload) {
113   - // 遍历所有解析器,找到能处理该主题的解析器
114   - for (Map.Entry<String, IotMessageParser> entry : parsers.entrySet()) {
115   - IotMessageParser parser = entry.getValue();
116   - if (parser.canHandle(topic)) {
117   - try {
118   - IotMqttMessage message = parser.parse(topic, payload);
119   - if (message != null) {
120   - log.debug(IotLogConstants.Converter.AUTO_SELECT_PROTOCOL, entry.getKey(), topic);
121   - return message;
122   - }
123   - } catch (Exception e) {
124   - log.debug(IotLogConstants.Converter.PROTOCOL_PARSE_FAILED_TRY_NEXT, entry.getKey(), topic);
125   - }
126   - }
127   - }
128   -
129   - log.warn(IotLogConstants.Converter.CANNOT_AUTO_RECOGNIZE_PROTOCOL, topic);
130   - return null;
131   - }
132   -}
133 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/enums/IotMessageDirectionEnum.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.enums;
2   -
3   -import lombok.AllArgsConstructor;
4   -import lombok.Getter;
5   -
6   -/**
7   - * IoT 消息方向枚举
8   - *
9   - * @author haohao
10   - */
11   -@Getter
12   -@AllArgsConstructor
13   -public enum IotMessageDirectionEnum {
14   -
15   - /**
16   - * 上行消息(设备到平台)
17   - */
18   - UPSTREAM("upstream", "上行"),
19   -
20   - /**
21   - * 下行消息(平台到设备)
22   - */
23   - DOWNSTREAM("downstream", "下行");
24   -
25   - /**
26   - * 方向编码
27   - */
28   - private final String code;
29   -
30   - /**
31   - * 方向名称
32   - */
33   - private final String name;
34   -
35   - /**
36   - * 根据编码获取消息方向
37   - *
38   - * @param code 方向编码
39   - * @return 消息方向枚举,如果未找到返回 null
40   - */
41   - public static IotMessageDirectionEnum getByCode(String code) {
42   - for (IotMessageDirectionEnum direction : values()) {
43   - if (direction.getCode().equals(code)) {
44   - return direction;
45   - }
46   - }
47   - return null;
48   - }
49   -}
50 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/enums/IotMessageTypeEnum.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.enums;
2   -
3   -import cn.iocoder.yudao.module.iot.protocol.constants.IotTopicConstants;
4   -import lombok.AllArgsConstructor;
5   -import lombok.Getter;
6   -
7   -/**
8   - * IoT 消息类型枚举
9   - *
10   - * @author haohao
11   - */
12   -@Getter
13   -@AllArgsConstructor
14   -public enum IotMessageTypeEnum {
15   -
16   - /**
17   - * 属性上报
18   - */
19   - PROPERTY_POST("property.post", "属性上报"),
20   -
21   - /**
22   - * 属性设置
23   - */
24   - PROPERTY_SET("property.set", "属性设置"),
25   -
26   - /**
27   - * 属性获取
28   - */
29   - PROPERTY_GET("property.get", "属性获取"),
30   -
31   - /**
32   - * 事件上报
33   - */
34   - EVENT_POST("event.post", "事件上报"),
35   -
36   - /**
37   - * 服务调用
38   - */
39   - SERVICE_INVOKE("service.invoke", "服务调用"),
40   -
41   - /**
42   - * 配置设置
43   - */
44   - CONFIG_SET("config.set", "配置设置"),
45   -
46   - /**
47   - * OTA 升级
48   - */
49   - OTA_UPGRADE("ota.upgrade", "OTA升级"),
50   -
51   - /**
52   - * 设备上线
53   - */
54   - DEVICE_ONLINE("device.online", "设备上线"),
55   -
56   - /**
57   - * 设备下线
58   - */
59   - DEVICE_OFFLINE("device.offline", "设备下线"),
60   -
61   - /**
62   - * 心跳
63   - */
64   - HEARTBEAT("heartbeat", "心跳");
65   -
66   - /**
67   - * 消息类型编码
68   - */
69   - private final String code;
70   -
71   - /**
72   - * 消息类型名称
73   - */
74   - private final String name;
75   -
76   - /**
77   - * 根据编码获取消息类型
78   - *
79   - * @param code 消息类型编码
80   - * @return 消息类型枚举,如果未找到返回 null
81   - */
82   - public static IotMessageTypeEnum getByCode(String code) {
83   - for (IotMessageTypeEnum type : values()) {
84   - if (type.getCode().equals(code)) {
85   - return type;
86   - }
87   - }
88   - return null;
89   - }
90   -
91   - /**
92   - * 根据方法名获取消息类型
93   - *
94   - * @param method 方法名
95   - * @return 消息类型枚举,如果未找到返回 null
96   - */
97   - public static IotMessageTypeEnum getByMethod(String method) {
98   - if (method == null) {
99   - return null;
100   - }
101   -
102   - // 处理 thing.service.xxx 格式
103   - if (method.startsWith(IotTopicConstants.MethodPrefix.THING_SERVICE)) {
104   - String servicePart = method.substring(IotTopicConstants.MethodPrefix.THING_SERVICE.length());
105   - if ("property.set".equals(servicePart)) {
106   - return PROPERTY_SET;
107   - } else if ("property.get".equals(servicePart)) {
108   - return PROPERTY_GET;
109   - } else if ("config.set".equals(servicePart)) {
110   - return CONFIG_SET;
111   - } else if ("ota.upgrade".equals(servicePart)) {
112   - return OTA_UPGRADE;
113   - } else {
114   - return SERVICE_INVOKE;
115   - }
116   - }
117   -
118   - // 处理 thing.event.xxx 格式
119   - if (method.startsWith(IotTopicConstants.MethodPrefix.THING_EVENT)) {
120   - String eventPart = method.substring(IotTopicConstants.MethodPrefix.THING_EVENT.length());
121   - if ("property.post".equals(eventPart)) {
122   - return PROPERTY_POST;
123   - } else {
124   - return EVENT_POST;
125   - }
126   - }
127   -
128   - // 其他类型
129   - switch (method) {
130   - case IotTopicConstants.Method.DEVICE_ONLINE:
131   - return DEVICE_ONLINE;
132   - case IotTopicConstants.Method.DEVICE_OFFLINE:
133   - return DEVICE_OFFLINE;
134   - case IotTopicConstants.Method.HEARTBEAT:
135   - return HEARTBEAT;
136   - default:
137   - return null;
138   - }
139   - }
140   -}
141 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/enums/IotProtocolTypeEnum.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.enums;
2   -
3   -import lombok.AllArgsConstructor;
4   -import lombok.Getter;
5   -
6   -/**
7   - * IoT 协议类型枚举
8   - *
9   - * @author haohao
10   - */
11   -@Getter
12   -@AllArgsConstructor
13   -public enum IotProtocolTypeEnum {
14   -
15   - /**
16   - * MQTT 协议(默认实现)
17   - */
18   - MQTT("mqtt", "MQTT 协议"),
19   -
20   - /**
21   - * MQTT 原始协议
22   - */
23   - MQTT_RAW("mqtt_raw", "MQTT 原始协议"),
24   -
25   - /**
26   - * HTTP 协议
27   - */
28   - HTTP("http", "HTTP 协议"),
29   -
30   - /**
31   - * TCP 协议
32   - */
33   - TCP("tcp", "TCP 协议"),
34   -
35   - /**
36   - * UDP 协议
37   - */
38   - UDP("udp", "UDP 协议"),
39   -
40   - /**
41   - * 自定义协议
42   - */
43   - CUSTOM("custom", "自定义协议");
44   -
45   - /**
46   - * 协议编码
47   - */
48   - private final String code;
49   -
50   - /**
51   - * 协议名称
52   - */
53   - private final String name;
54   -
55   - /**
56   - * 根据编码获取协议类型
57   - *
58   - * @param code 协议编码
59   - * @return 协议类型枚举,如果未找到返回 null
60   - */
61   - public static IotProtocolTypeEnum getByCode(String code) {
62   - for (IotProtocolTypeEnum type : values()) {
63   - if (type.getCode().equals(code)) {
64   - return type;
65   - }
66   - }
67   - return null;
68   - }
69   -
70   - /**
71   - * 检查是否为有效的协议编码
72   - *
73   - * @param code 协议编码
74   - * @return 如果有效返回 true,否则返回 false
75   - */
76   - public static boolean isValidCode(String code) {
77   - return getByCode(code) != null;
78   - }
79   -}
80 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/IotMessageParser.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.message;
2   -
3   -/**
4   - * IoT 消息解析器接口
5   - * <p>
6   - * 用于解析不同协议的消息内容
7   - *
8   - * @author haohao
9   - */
10   -public interface IotMessageParser {
11   -
12   - /**
13   - * 解析消息
14   - *
15   - * @param topic 主题
16   - * @param payload 消息负载
17   - * @return 解析后的标准消息,如果解析失败返回 null
18   - */
19   - IotMqttMessage parse(String topic, byte[] payload);
20   -
21   - /**
22   - * 格式化响应消息
23   - *
24   - * @param response 标准响应
25   - * @return 格式化后的响应字节数组
26   - */
27   - byte[] formatResponse(IotStandardResponse response);
28   -
29   - /**
30   - * 检查是否能够处理指定主题的消息
31   - *
32   - * @param topic 主题
33   - * @return 如果能处理返回 true,否则返回 false
34   - */
35   - boolean canHandle(String topic);
36   -}
37 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/IotMqttMessage.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.message;
2   -
3   -import cn.hutool.core.util.IdUtil;
4   -import cn.hutool.json.JSONObject;
5   -import lombok.Builder;
6   -import lombok.Data;
7   -
8   -import java.util.Map;
9   -
10   -/**
11   - * IoT MQTT 消息模型
12   - * <p>
13   - * 基于 MQTT 协议规范实现的标准消息格式,支持设备属性、事件、服务调用等标准功能
14   - *
15   - * @author haohao
16   - * @see <a href="https://mqtt.org/">MQTT 协议官方规范</a>
17   - */
18   -@Data
19   -@Builder
20   -public class IotMqttMessage {
21   -
22   - /**
23   - * 消息 ID
24   - */
25   - private String id;
26   -
27   - /**
28   - * 协议版本
29   - */
30   - @Builder.Default
31   - private String version = "1.0";
32   -
33   - /**
34   - * 消息方法
35   - */
36   - private String method;
37   -
38   - /**
39   - * 消息参数
40   - */
41   - private Map<String, Object> params;
42   -
43   - /**
44   - * 转换为 JSONObject
45   - *
46   - * @return JSONObject 对象
47   - */
48   - public JSONObject toJsonObject() {
49   - JSONObject json = new JSONObject();
50   - json.set("id", id);
51   - json.set("version", version);
52   - json.set("method", method);
53   - json.set("params", params != null ? params : new JSONObject());
54   - return json;
55   - }
56   -
57   - /**
58   - * 转换为 JSON 字符串
59   - *
60   - * @return JSON 字符串
61   - */
62   - public String toJsonString() {
63   - return toJsonObject().toString();
64   - }
65   -
66   - /**
67   - * 创建设备服务调用消息
68   - *
69   - * @param requestId 请求 ID,为空时自动生成
70   - * @param serviceIdentifier 服务标识符
71   - * @param params 服务参数
72   - * @return MQTT 消息对象
73   - */
74   - public static IotMqttMessage createServiceInvokeMessage(String requestId, String serviceIdentifier,
75   - Map<String, Object> params) {
76   - return IotMqttMessage.builder()
77   - .id(requestId != null ? requestId : generateRequestId())
78   - .method("thing.service." + serviceIdentifier)
79   - .params(params)
80   - .build();
81   - }
82   -
83   - /**
84   - * 创建设备属性设置消息
85   - *
86   - * @param requestId 请求 ID,为空时自动生成
87   - * @param properties 设备属性
88   - * @return MQTT 消息对象
89   - */
90   - public static IotMqttMessage createPropertySetMessage(String requestId, Map<String, Object> properties) {
91   - return IotMqttMessage.builder()
92   - .id(requestId != null ? requestId : generateRequestId())
93   - .method("thing.service.property.set")
94   - .params(properties)
95   - .build();
96   - }
97   -
98   - /**
99   - * 创建设备属性获取消息
100   - *
101   - * @param requestId 请求 ID,为空时自动生成
102   - * @param identifiers 要获取的属性标识符列表
103   - * @return MQTT 消息对象
104   - */
105   - public static IotMqttMessage createPropertyGetMessage(String requestId, String[] identifiers) {
106   - JSONObject params = new JSONObject();
107   - params.set("identifiers", identifiers);
108   -
109   - return IotMqttMessage.builder()
110   - .id(requestId != null ? requestId : generateRequestId())
111   - .method("thing.service.property.get")
112   - .params(params)
113   - .build();
114   - }
115   -
116   - /**
117   - * 创建设备配置设置消息
118   - *
119   - * @param requestId 请求 ID,为空时自动生成
120   - * @param configs 设备配置
121   - * @return MQTT 消息对象
122   - */
123   - public static IotMqttMessage createConfigSetMessage(String requestId, Map<String, Object> configs) {
124   - return IotMqttMessage.builder()
125   - .id(requestId != null ? requestId : generateRequestId())
126   - .method("thing.service.config.set")
127   - .params(configs)
128   - .build();
129   - }
130   -
131   - /**
132   - * 创建设备 OTA 升级消息
133   - *
134   - * @param requestId 请求 ID,为空时自动生成
135   - * @param otaInfo OTA 升级信息
136   - * @return MQTT 消息对象
137   - */
138   - public static IotMqttMessage createOtaUpgradeMessage(String requestId, Map<String, Object> otaInfo) {
139   - return IotMqttMessage.builder()
140   - .id(requestId != null ? requestId : generateRequestId())
141   - .method("thing.service.ota.upgrade")
142   - .params(otaInfo)
143   - .build();
144   - }
145   -
146   - /**
147   - * 生成请求 ID
148   - *
149   - * @return 请求 ID
150   - */
151   - public static String generateRequestId() {
152   - return IdUtil.fastSimpleUUID();
153   - }
154   -}
155 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/IotStandardResponse.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.message;
2   -
3   -import cn.hutool.core.util.StrUtil;
4   -import lombok.Data;
5   -import lombok.experimental.Accessors;
6   -
7   -/**
8   - * IoT 标准协议响应实体类
9   - * <p>
10   - * 用于统一 MQTT 和 HTTP 的响应格式
11   - *
12   - * @author haohao
13   - */
14   -@Data
15   -@Accessors(chain = true)
16   -public class IotStandardResponse {
17   -
18   - /**
19   - * 消息 ID
20   - */
21   - private String id;
22   -
23   - /**
24   - * 状态码
25   - */
26   - private Integer code;
27   -
28   - /**
29   - * 响应数据
30   - */
31   - private Object data;
32   -
33   - /**
34   - * 响应消息
35   - */
36   - private String message;
37   -
38   - /**
39   - * 方法名
40   - */
41   - private String method;
42   -
43   - /**
44   - * 协议版本
45   - */
46   - private String version;
47   -
48   - /**
49   - * 创建成功响应
50   - *
51   - * @param id 消息 ID
52   - * @param method 方法名
53   - * @return 成功响应
54   - */
55   - public static IotStandardResponse success(String id, String method) {
56   - return success(id, method, null);
57   - }
58   -
59   - /**
60   - * 创建成功响应
61   - *
62   - * @param id 消息 ID
63   - * @param method 方法名
64   - * @param data 响应数据
65   - * @return 成功响应
66   - */
67   - public static IotStandardResponse success(String id, String method, Object data) {
68   - return new IotStandardResponse()
69   - .setId(id)
70   - .setCode(200)
71   - .setData(data)
72   - .setMessage("success")
73   - .setMethod(method)
74   - .setVersion("1.0");
75   - }
76   -
77   - /**
78   - * 创建错误响应
79   - *
80   - * @param id 消息 ID
81   - * @param method 方法名
82   - * @param code 错误码
83   - * @param message 错误消息
84   - * @return 错误响应
85   - */
86   - public static IotStandardResponse error(String id, String method, Integer code, String message) {
87   - return new IotStandardResponse()
88   - .setId(id)
89   - .setCode(code)
90   - .setData(null)
91   - .setMessage(StrUtil.blankToDefault(message, "error"))
92   - .setMethod(method)
93   - .setVersion("1.0");
94   - }
95   -}
96 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotHttpMessageParser.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.message.impl;
2   -
3   -import cn.hutool.core.util.StrUtil;
4   -import cn.hutool.json.JSONObject;
5   -import cn.hutool.json.JSONUtil;
6   -import cn.iocoder.yudao.module.iot.protocol.constants.IotHttpConstants;
7   -import cn.iocoder.yudao.module.iot.protocol.constants.IotLogConstants;
8   -import cn.iocoder.yudao.module.iot.protocol.constants.IotTopicConstants;
9   -import cn.iocoder.yudao.module.iot.protocol.message.IotMessageParser;
10   -import cn.iocoder.yudao.module.iot.protocol.message.IotMqttMessage;
11   -import cn.iocoder.yudao.module.iot.protocol.message.IotStandardResponse;
12   -import lombok.extern.slf4j.Slf4j;
13   -
14   -import java.nio.charset.StandardCharsets;
15   -import java.util.HashMap;
16   -import java.util.Map;
17   -
18   -/**
19   - * IoT HTTP 协议消息解析器实现
20   - * <p>
21   - * 参考阿里云IoT平台HTTPS协议标准,支持设备认证和数据上报两种消息类型:
22   - * <p>
23   - * 1. 设备认证消息格式:
24   - *
25   - * <pre>
26   - * POST /auth HTTP/1.1
27   - * Content-Type: application/json
28   - * {
29   - * "productKey": "a1AbC***",
30   - * "deviceName": "device01",
31   - * "clientId": "device01_001",
32   - * "timestamp": "1501668289957",
33   - * "sign": "xxxxx",
34   - * "signmethod": "hmacsha1",
35   - * "version": "default"
36   - * }
37   - * </pre>
38   - * <p>
39   - * 2. 数据上报消息格式:
40   - *
41   - * <pre>
42   - * POST /topic/${topic} HTTP/1.1
43   - * password: ${token}
44   - * Content-Type: application/octet-stream
45   - * ${payload}
46   - * </pre>
47   - *
48   - * @author haohao
49   - */
50   -@Slf4j
51   -public class IotHttpMessageParser implements IotMessageParser {
52   -
53   - /**
54   - * 认证路径
55   - */
56   - public static final String AUTH_PATH = IotHttpConstants.Path.AUTH;
57   -
58   - /**
59   - * 主题路径前缀
60   - */
61   - public static final String TOPIC_PATH_PREFIX = IotHttpConstants.Path.TOPIC_PREFIX;
62   -
63   - @Override
64   - public IotMqttMessage parse(String topic, byte[] payload) {
65   - if (payload == null || payload.length == 0) {
66   - log.warn(IotLogConstants.Http.RECEIVED_EMPTY_MESSAGE, topic);
67   - return null;
68   - }
69   -
70   - try {
71   - String message = new String(payload, StandardCharsets.UTF_8);
72   -
73   - // 判断是认证请求还是数据上报
74   - if (AUTH_PATH.equals(topic)) {
75   - return parseAuthMessage(message);
76   - } else if (topic.startsWith(TOPIC_PATH_PREFIX)) {
77   - return parseDataMessage(topic, message);
78   - } else {
79   - log.warn(IotLogConstants.Http.UNSUPPORTED_PATH_FORMAT, topic);
80   - return null;
81   - }
82   -
83   - } catch (Exception e) {
84   - log.error(IotLogConstants.Http.PARSE_MESSAGE_FAILED, topic, e);
85   - return null;
86   - }
87   - }
88   -
89   - /**
90   - * 解析设备认证消息
91   - *
92   - * @param message 认证消息JSON
93   - * @return 标准消息格式
94   - */
95   - private IotMqttMessage parseAuthMessage(String message) {
96   - if (!JSONUtil.isTypeJSON(message)) {
97   - log.warn(IotLogConstants.Http.AUTH_MESSAGE_NOT_JSON, message);
98   - return null;
99   - }
100   -
101   - JSONObject json = JSONUtil.parseObj(message);
102   -
103   - // 验证必需字段
104   - String productKey = json.getStr(IotHttpConstants.AuthField.PRODUCT_KEY);
105   - String deviceName = json.getStr(IotHttpConstants.AuthField.DEVICE_NAME);
106   - String clientId = json.getStr(IotHttpConstants.AuthField.CLIENT_ID);
107   - String sign = json.getStr(IotHttpConstants.AuthField.SIGN);
108   -
109   - if (StrUtil.hasBlank(productKey, deviceName, clientId, sign)) {
110   - log.warn(IotLogConstants.Http.AUTH_MESSAGE_MISSING_REQUIRED_FIELDS, message);
111   - return null;
112   - }
113   -
114   - // 构建认证消息
115   - Map<String, Object> params = new HashMap<>();
116   - params.put(IotHttpConstants.AuthField.PRODUCT_KEY, productKey);
117   - params.put(IotHttpConstants.AuthField.DEVICE_NAME, deviceName);
118   - params.put(IotHttpConstants.AuthField.CLIENT_ID, clientId);
119   - params.put(IotHttpConstants.AuthField.TIMESTAMP, json.getStr(IotHttpConstants.AuthField.TIMESTAMP));
120   - params.put(IotHttpConstants.AuthField.SIGN, sign);
121   - params.put(IotHttpConstants.AuthField.SIGN_METHOD,
122   - json.getStr(IotHttpConstants.AuthField.SIGN_METHOD, IotHttpConstants.DefaultValue.SIGN_METHOD));
123   -
124   - return IotMqttMessage.builder()
125   - .id(generateMessageId())
126   - .method(IotHttpConstants.Method.DEVICE_AUTH)
127   - .version(json.getStr(IotHttpConstants.AuthField.VERSION, IotHttpConstants.DefaultValue.VERSION))
128   - .params(params)
129   - .build();
130   - }
131   -
132   - /**
133   - * 解析数据上报消息
134   - *
135   - * @param topic 主题路径,格式:/topic/${actualTopic}
136   - * @param message 消息内容
137   - * @return 标准消息格式
138   - */
139   - private IotMqttMessage parseDataMessage(String topic, String message) {
140   - // 提取实际的主题,去掉 /topic 前缀
141   - String actualTopic = topic.substring(TOPIC_PATH_PREFIX.length()); // 直接移除/topic前缀
142   -
143   - // 尝试解析为JSON格式
144   - if (JSONUtil.isTypeJSON(message)) {
145   - return parseJsonDataMessage(actualTopic, message);
146   - } else {
147   - // 原始数据格式
148   - return parseRawDataMessage(actualTopic, message);
149   - }
150   - }
151   -
152   - /**
153   - * 解析JSON格式的数据消息
154   - *
155   - * @param topic 实际主题
156   - * @param message JSON消息
157   - * @return 标准消息格式
158   - */
159   - private IotMqttMessage parseJsonDataMessage(String topic, String message) {
160   - JSONObject json = JSONUtil.parseObj(message);
161   -
162   - // 生成消息ID
163   - String messageId = json.getStr(IotHttpConstants.MessageField.ID);
164   - if (StrUtil.isBlank(messageId)) {
165   - messageId = generateMessageId();
166   - }
167   -
168   - // 获取方法名
169   - String method = json.getStr(IotHttpConstants.MessageField.METHOD);
170   - if (StrUtil.isBlank(method)) {
171   - // 根据主题推断方法名
172   - method = inferMethodFromTopic(topic);
173   - }
174   -
175   - // 获取参数
176   - Object params = json.get(IotHttpConstants.MessageField.PARAMS);
177   - Map<String, Object> paramsMap = new HashMap<>();
178   - if (params instanceof Map) {
179   - paramsMap.putAll((Map<String, Object>) params);
180   - } else if (params != null) {
181   - paramsMap.put(IotHttpConstants.MessageField.DATA, params);
182   - }
183   -
184   - return IotMqttMessage.builder()
185   - .id(messageId)
186   - .method(method)
187   - .version(json.getStr(IotHttpConstants.MessageField.VERSION,
188   - IotHttpConstants.DefaultValue.MESSAGE_VERSION))
189   - .params(paramsMap)
190   - .build();
191   - }
192   -
193   - /**
194   - * 解析原始数据消息
195   - *
196   - * @param topic 实际主题
197   - * @param message 原始消息
198   - * @return 标准消息格式
199   - */
200   - private IotMqttMessage parseRawDataMessage(String topic, String message) {
201   - Map<String, Object> params = new HashMap<>();
202   - params.put(IotHttpConstants.MessageField.DATA, message);
203   -
204   - return IotMqttMessage.builder()
205   - .id(generateMessageId())
206   - .method(inferMethodFromTopic(topic))
207   - .version(IotHttpConstants.DefaultValue.MESSAGE_VERSION)
208   - .params(params)
209   - .build();
210   - }
211   -
212   - /**
213   - * 根据主题推断方法名
214   - *
215   - * @param topic 主题
216   - * @return 方法名
217   - */
218   - private String inferMethodFromTopic(String topic) {
219   - if (StrUtil.isBlank(topic)) {
220   - return IotHttpConstants.DefaultValue.UNKNOWN_METHOD;
221   - }
222   -
223   - // 标准系统主题解析
224   - if (topic.startsWith(IotTopicConstants.SYS_TOPIC_PREFIX)) {
225   - if (topic.contains(IotTopicConstants.PROPERTY_SET_TOPIC)) {
226   - return IotTopicConstants.Method.PROPERTY_SET;
227   - } else if (topic.contains(IotTopicConstants.PROPERTY_GET_TOPIC)) {
228   - return IotTopicConstants.Method.PROPERTY_GET;
229   - } else if (topic.contains(IotTopicConstants.PROPERTY_POST_TOPIC)) {
230   - return IotTopicConstants.Method.PROPERTY_POST;
231   - } else if (topic.contains(IotTopicConstants.EVENT_POST_TOPIC_PREFIX)
232   - && topic.endsWith(IotTopicConstants.EVENT_POST_TOPIC_SUFFIX)) {
233   - // 自定义事件上报
234   - String[] parts = topic.split("/");
235   - // 查找event关键字的位置
236   - for (int i = 0; i < parts.length; i++) {
237   - if (IotTopicConstants.Keyword.EVENT.equals(parts[i]) && i + 1 < parts.length) {
238   - String eventId = parts[i + 1];
239   - return IotTopicConstants.MethodPrefix.THING_EVENT + eventId + ".post";
240   - }
241   - }
242   - } else if (topic.contains(IotTopicConstants.SERVICE_TOPIC_PREFIX)
243   - && !topic.contains(IotTopicConstants.Keyword.PROPERTY)) {
244   - // 自定义服务调用
245   - String[] parts = topic.split("/");
246   - // 查找service关键字的位置
247   - for (int i = 0; i < parts.length; i++) {
248   - if (IotTopicConstants.Keyword.SERVICE.equals(parts[i]) && i + 1 < parts.length) {
249   - String serviceId = parts[i + 1];
250   - return IotTopicConstants.MethodPrefix.THING_SERVICE + serviceId;
251   - }
252   - }
253   - }
254   - }
255   -
256   - // 自定义主题
257   - return IotHttpConstants.Method.CUSTOM_MESSAGE;
258   - }
259   -
260   - /**
261   - * 生成消息ID
262   - *
263   - * @return 消息ID
264   - */
265   - private String generateMessageId() {
266   - return IotMqttMessage.generateRequestId();
267   - }
268   -
269   - @Override
270   - public byte[] formatResponse(IotStandardResponse response) {
271   - try {
272   - JSONObject httpResponse = new JSONObject();
273   -
274   - // 判断是否为认证响应
275   - if (IotHttpConstants.Method.DEVICE_AUTH.equals(response.getMethod())) {
276   - // 认证响应格式
277   - httpResponse.set(IotHttpConstants.ResponseField.CODE, response.getCode());
278   - httpResponse.set(IotHttpConstants.ResponseField.MESSAGE, response.getMessage());
279   -
280   - if (response.getCode() == 200 && response.getData() != null) {
281   - JSONObject info = new JSONObject();
282   - if (response.getData() instanceof Map) {
283   - Map<String, Object> dataMap = (Map<String, Object>) response.getData();
284   - info.putAll(dataMap);
285   - } else {
286   - info.set(IotHttpConstants.ResponseField.TOKEN, response.getData().toString());
287   - }
288   - httpResponse.set(IotHttpConstants.ResponseField.INFO, info);
289   - }
290   - } else {
291   - // 数据上报响应格式
292   - httpResponse.set(IotHttpConstants.ResponseField.CODE, response.getCode());
293   - httpResponse.set(IotHttpConstants.ResponseField.MESSAGE, response.getMessage());
294   -
295   - if (response.getCode() == 200) {
296   - JSONObject info = new JSONObject();
297   - info.set(IotHttpConstants.ResponseField.MESSAGE_ID, response.getId());
298   - httpResponse.set(IotHttpConstants.ResponseField.INFO, info);
299   - }
300   - }
301   -
302   - String json = httpResponse.toString();
303   - return json.getBytes(StandardCharsets.UTF_8);
304   - } catch (Exception e) {
305   - log.error(IotLogConstants.Http.FORMAT_RESPONSE_FAILED, e);
306   - return new byte[0];
307   - }
308   - }
309   -
310   - @Override
311   - public boolean canHandle(String topic) {
312   - // 支持认证路径和主题路径
313   - return topic != null && (AUTH_PATH.equals(topic) || topic.startsWith(TOPIC_PATH_PREFIX));
314   - }
315   -
316   - /**
317   - * 从设备标识中解析产品Key和设备名称
318   - *
319   - * @param deviceKey 设备标识,格式:productKey/deviceName
320   - * @return 包含产品Key和设备名称的数组,[0]为产品Key,[1]为设备名称
321   - */
322   - public static String[] parseDeviceKey(String deviceKey) {
323   - if (StrUtil.isBlank(deviceKey)) {
324   - return null;
325   - }
326   -
327   - String[] parts = deviceKey.split("/");
328   - if (parts.length != 2) {
329   - return null;
330   - }
331   -
332   - return new String[]{parts[0], parts[1]};
333   - }
334   -
335   - /**
336   - * 构建设备标识
337   - *
338   - * @param productKey 产品Key
339   - * @param deviceName 设备名称
340   - * @return 设备标识,格式:productKey/deviceName
341   - */
342   - public static String buildDeviceKey(String productKey, String deviceName) {
343   - if (StrUtil.isBlank(productKey) || StrUtil.isBlank(deviceName)) {
344   - return null;
345   - }
346   - return productKey + "/" + deviceName;
347   - }
348   -}
349 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotMqttMessageParser.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.message.impl;
2   -
3   -import cn.hutool.core.util.StrUtil;
4   -import cn.hutool.json.JSONObject;
5   -import cn.hutool.json.JSONUtil;
6   -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
7   -import cn.iocoder.yudao.module.iot.protocol.message.IotMessageParser;
8   -import cn.iocoder.yudao.module.iot.protocol.message.IotMqttMessage;
9   -import cn.iocoder.yudao.module.iot.protocol.message.IotStandardResponse;
10   -import cn.iocoder.yudao.module.iot.protocol.util.IotTopicUtils;
11   -import lombok.extern.slf4j.Slf4j;
12   -
13   -import java.nio.charset.StandardCharsets;
14   -import java.util.Map;
15   -
16   -/**
17   - * IoT MQTT 协议消息解析器实现
18   - * <p>
19   - * 基于 MQTT 协议规范实现的消息解析器,支持设备属性、事件、服务调用等标准功能
20   - *
21   - * @author haohao
22   - */
23   -@Slf4j
24   -public class IotMqttMessageParser implements IotMessageParser {
25   -
26   - @Override
27   - public IotMqttMessage parse(String topic, byte[] payload) {
28   - if (payload == null || payload.length == 0) {
29   - log.warn("[MQTT] 收到空消息内容, topic={}", topic);
30   - return null;
31   - }
32   -
33   - try {
34   - String message = new String(payload, StandardCharsets.UTF_8);
35   - if (!JSONUtil.isTypeJSON(message)) {
36   - log.warn("[MQTT] 收到非JSON格式消息, topic={}, message={}", topic, message);
37   - return null;
38   - }
39   -
40   - JSONObject json = JSONUtil.parseObj(message);
41   - String id = json.getStr("id");
42   - String method = json.getStr("method");
43   -
44   - if (StrUtil.isBlank(method)) {
45   - // 尝试从 topic 中解析方法
46   - method = IotTopicUtils.parseMethodFromTopic(topic);
47   - if (StrUtil.isBlank(method)) {
48   - log.warn("[MQTT] 无法确定消息方法, topic={}, message={}", topic, message);
49   - return null;
50   - }
51   - }
52   -
53   - @SuppressWarnings("unchecked")
54   - Map<String, Object> params = (Map<String, Object>) json.getObj("params", Map.class);
55   - return IotMqttMessage.builder()
56   - .id(id)
57   - .method(method)
58   - .version(json.getStr("version", "1.0"))
59   - .params(params)
60   - .build();
61   - } catch (Exception e) {
62   - log.error("[MQTT] 解析消息失败, topic={}", topic, e);
63   - return null;
64   - }
65   - }
66   -
67   - @Override
68   - public byte[] formatResponse(IotStandardResponse response) {
69   - try {
70   - String json = JsonUtils.toJsonString(response);
71   - return json.getBytes(StandardCharsets.UTF_8);
72   - } catch (Exception e) {
73   - log.error("[MQTT] 格式化响应失败", e);
74   - return new byte[0];
75   - }
76   - }
77   -
78   - @Override
79   - public boolean canHandle(String topic) {
80   - // MQTT 协议支持更多主题格式
81   - return topic != null && (
82   - topic.startsWith("/sys/") || // 兼容现有系统主题
83   - topic.startsWith("/mqtt/") || // 新的通用 MQTT 主题
84   - topic.startsWith("/device/") // 设备主题
85   - );
86   - }
87   -}
88 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/util/IotHttpTopicUtils.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.util;
2   -
3   -import cn.hutool.core.util.StrUtil;
4   -
5   -/**
6   - * IoT HTTP 协议主题工具类
7   - * <p>
8   - * 参考阿里云IoT平台HTTPS协议标准,支持以下路径格式:
9   - * 1. 设备认证:/auth
10   - * 2. 数据上报:/topic/${actualTopic}
11   - * <p>
12   - * 其中 actualTopic 遵循MQTT主题规范,例如:
13   - * - /sys/{productKey}/{deviceName}/thing/service/property/set
14   - * - /{productKey}/{deviceName}/user/get
15   - *
16   - * @author haohao
17   - */
18   -public class IotHttpTopicUtils {
19   -
20   - /**
21   - * 设备认证路径
22   - */
23   - public static final String AUTH_PATH = "/auth";
24   -
25   - /**
26   - * 数据上报路径前缀
27   - */
28   - public static final String TOPIC_PATH_PREFIX = "/topic";
29   -
30   - /**
31   - * 系统主题前缀
32   - */
33   - public static final String SYS_TOPIC_PREFIX = "/sys";
34   -
35   - /**
36   - * 构建设备认证路径
37   - *
38   - * @return 认证路径
39   - */
40   - public static String buildAuthPath() {
41   - return AUTH_PATH;
42   - }
43   -
44   - /**
45   - * 构建数据上报路径
46   - *
47   - * @param actualTopic 实际的MQTT主题
48   - * @return HTTP数据上报路径
49   - */
50   - public static String buildTopicPath(String actualTopic) {
51   - if (StrUtil.isBlank(actualTopic)) {
52   - return null;
53   - }
54   - return TOPIC_PATH_PREFIX + actualTopic;
55   - }
56   -
57   - /**
58   - * 构建系统属性设置路径
59   - *
60   - * @param productKey 产品Key
61   - * @param deviceName 设备名称
62   - * @return HTTP路径
63   - */
64   - public static String buildPropertySetPath(String productKey, String deviceName) {
65   - if (StrUtil.hasBlank(productKey, deviceName)) {
66   - return null;
67   - }
68   - String actualTopic = SYS_TOPIC_PREFIX + "/" + productKey + "/" + deviceName + "/thing/service/property/set";
69   - return buildTopicPath(actualTopic);
70   - }
71   -
72   - /**
73   - * 构建系统属性获取路径
74   - *
75   - * @param productKey 产品Key
76   - * @param deviceName 设备名称
77   - * @return HTTP路径
78   - */
79   - public static String buildPropertyGetPath(String productKey, String deviceName) {
80   - if (StrUtil.hasBlank(productKey, deviceName)) {
81   - return null;
82   - }
83   - String actualTopic = SYS_TOPIC_PREFIX + "/" + productKey + "/" + deviceName + "/thing/service/property/get";
84   - return buildTopicPath(actualTopic);
85   - }
86   -
87   - /**
88   - * 构建系统属性上报路径
89   - *
90   - * @param productKey 产品Key
91   - * @param deviceName 设备名称
92   - * @return HTTP路径
93   - */
94   - public static String buildPropertyPostPath(String productKey, String deviceName) {
95   - if (StrUtil.hasBlank(productKey, deviceName)) {
96   - return null;
97   - }
98   - String actualTopic = SYS_TOPIC_PREFIX + "/" + productKey + "/" + deviceName + "/thing/event/property/post";
99   - return buildTopicPath(actualTopic);
100   - }
101   -
102   - /**
103   - * 构建系统事件上报路径
104   - *
105   - * @param productKey 产品Key
106   - * @param deviceName 设备名称
107   - * @param eventIdentifier 事件标识符
108   - * @return HTTP路径
109   - */
110   - public static String buildEventPostPath(String productKey, String deviceName, String eventIdentifier) {
111   - if (StrUtil.hasBlank(productKey, deviceName, eventIdentifier)) {
112   - return null;
113   - }
114   - String actualTopic = SYS_TOPIC_PREFIX + "/" + productKey + "/" + deviceName + "/thing/event/" + eventIdentifier
115   - + "/post";
116   - return buildTopicPath(actualTopic);
117   - }
118   -
119   - /**
120   - * 构建系统服务调用路径
121   - *
122   - * @param productKey 产品Key
123   - * @param deviceName 设备名称
124   - * @param serviceIdentifier 服务标识符
125   - * @return HTTP路径
126   - */
127   - public static String buildServiceInvokePath(String productKey, String deviceName, String serviceIdentifier) {
128   - if (StrUtil.hasBlank(productKey, deviceName, serviceIdentifier)) {
129   - return null;
130   - }
131   - String actualTopic = SYS_TOPIC_PREFIX + "/" + productKey + "/" + deviceName + "/thing/service/"
132   - + serviceIdentifier;
133   - return buildTopicPath(actualTopic);
134   - }
135   -
136   - /**
137   - * 构建自定义主题路径
138   - *
139   - * @param productKey 产品Key
140   - * @param deviceName 设备名称
141   - * @param customPath 自定义路径
142   - * @return HTTP路径
143   - */
144   - public static String buildCustomTopicPath(String productKey, String deviceName, String customPath) {
145   - if (StrUtil.hasBlank(productKey, deviceName, customPath)) {
146   - return null;
147   - }
148   - String actualTopic = "/" + productKey + "/" + deviceName + "/" + customPath;
149   - return buildTopicPath(actualTopic);
150   - }
151   -
152   - /**
153   - * 从HTTP路径中提取实际主题
154   - *
155   - * @param httpPath HTTP路径,格式:/topic/${actualTopic}
156   - * @return 实际主题,如果解析失败返回null
157   - */
158   - public static String extractActualTopic(String httpPath) {
159   - if (StrUtil.isBlank(httpPath) || !httpPath.startsWith(TOPIC_PATH_PREFIX)) {
160   - return null;
161   - }
162   - return httpPath.substring(TOPIC_PATH_PREFIX.length()); // 直接移除/topic前缀
163   - }
164   -
165   - /**
166   - * 从主题中解析产品Key
167   - *
168   - * @param topic 主题,支持系统主题和自定义主题
169   - * @return 产品Key,如果无法解析则返回null
170   - */
171   - public static String parseProductKeyFromTopic(String topic) {
172   - if (StrUtil.isBlank(topic)) {
173   - return null;
174   - }
175   -
176   - String[] parts = topic.split("/");
177   -
178   - // 系统主题格式:/sys/{productKey}/{deviceName}/...
179   - if (parts.length >= 4 && "sys".equals(parts[1])) {
180   - return parts[2];
181   - }
182   -
183   - // 自定义主题格式:/{productKey}/{deviceName}/...
184   - // 确保不是不完整的系统主题格式
185   - if (parts.length >= 3 && StrUtil.isNotBlank(parts[1]) && !"sys".equals(parts[1])) {
186   - return parts[1];
187   - }
188   -
189   - return null;
190   - }
191   -
192   - /**
193   - * 从主题中解析设备名称
194   - *
195   - * @param topic 主题,支持系统主题和自定义主题
196   - * @return 设备名称,如果无法解析则返回null
197   - */
198   - public static String parseDeviceNameFromTopic(String topic) {
199   - if (StrUtil.isBlank(topic)) {
200   - return null;
201   - }
202   -
203   - String[] parts = topic.split("/");
204   -
205   - // 系统主题格式:/sys/{productKey}/{deviceName}/...
206   - if (parts.length >= 4 && "sys".equals(parts[1])) {
207   - return parts[3];
208   - }
209   -
210   - // 自定义主题格式:/{productKey}/{deviceName}/...
211   - // 确保不是不完整的系统主题格式
212   - if (parts.length >= 3 && StrUtil.isNotBlank(parts[2]) && !"sys".equals(parts[1])) {
213   - return parts[2];
214   - }
215   -
216   - return null;
217   - }
218   -
219   - /**
220   - * 检查是否为认证路径
221   - *
222   - * @param path 路径
223   - * @return 如果是认证路径返回true,否则返回false
224   - */
225   - public static boolean isAuthPath(String path) {
226   - return AUTH_PATH.equals(path);
227   - }
228   -
229   - /**
230   - * 检查是否为数据上报路径
231   - *
232   - * @param path 路径
233   - * @return 如果是数据上报路径返回true,否则返回false
234   - */
235   - public static boolean isTopicPath(String path) {
236   - return path != null && path.startsWith(TOPIC_PATH_PREFIX);
237   - }
238   -
239   - /**
240   - * 检查是否为有效的HTTP路径
241   - *
242   - * @param path 路径
243   - * @return 如果是有效的HTTP路径返回true,否则返回false
244   - */
245   - public static boolean isValidHttpPath(String path) {
246   - return isAuthPath(path) || isTopicPath(path);
247   - }
248   -
249   - /**
250   - * 检查是否为系统主题
251   - *
252   - * @param topic 主题
253   - * @return 如果是系统主题返回true,否则返回false
254   - */
255   - public static boolean isSystemTopic(String topic) {
256   - return topic != null && topic.startsWith(SYS_TOPIC_PREFIX);
257   - }
258   -
259   - /**
260   - * 构建响应主题路径
261   - *
262   - * @param requestPath 请求路径
263   - * @return 响应路径,如果无法构建返回null
264   - */
265   - public static String buildReplyPath(String requestPath) {
266   - String actualTopic = extractActualTopic(requestPath);
267   - if (actualTopic == null) {
268   - return null;
269   - }
270   -
271   - // 为系统主题添加_reply后缀
272   - if (isSystemTopic(actualTopic)) {
273   - String replyTopic = actualTopic + "_reply";
274   - return buildTopicPath(replyTopic);
275   - }
276   -
277   - return null;
278   - }
279   -}
280 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/util/IotTopicParser.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.util;
2   -
3   -import cn.hutool.core.util.StrUtil;
4   -import cn.iocoder.yudao.module.iot.protocol.constants.IotTopicConstants;
5   -import cn.iocoder.yudao.module.iot.protocol.enums.IotMessageDirectionEnum;
6   -import cn.iocoder.yudao.module.iot.protocol.enums.IotMessageTypeEnum;
7   -import lombok.Data;
8   -
9   -/**
10   - * IoT 主题解析器
11   - * <p>
12   - * 用于解析各种格式的 IoT 主题,提取其中的关键信息
13   - *
14   - * @author haohao
15   - */
16   -public class IotTopicParser {
17   -
18   - /**
19   - * 主题解析结果
20   - */
21   - @Data
22   - public static class TopicInfo {
23   - /**
24   - * 产品Key
25   - */
26   - private String productKey;
27   -
28   - /**
29   - * 设备名称
30   - */
31   - private String deviceName;
32   -
33   - /**
34   - * 消息类型
35   - */
36   - private IotMessageTypeEnum messageType;
37   -
38   - /**
39   - * 消息方向
40   - */
41   - private IotMessageDirectionEnum direction;
42   -
43   - /**
44   - * 服务标识符(仅服务调用时有效)
45   - */
46   - private String serviceIdentifier;
47   -
48   - /**
49   - * 事件标识符(仅事件上报时有效)
50   - */
51   - private String eventIdentifier;
52   -
53   - /**
54   - * 是否为响应主题
55   - */
56   - private boolean isReply;
57   -
58   - /**
59   - * 原始主题
60   - */
61   - private String originalTopic;
62   - }
63   -
64   - /**
65   - * 解析主题
66   - *
67   - * @param topic 主题字符串
68   - * @return 解析结果,如果解析失败返回 null
69   - */
70   - public static TopicInfo parse(String topic) {
71   - if (StrUtil.isBlank(topic)) {
72   - return null;
73   - }
74   -
75   - TopicInfo info = new TopicInfo();
76   - info.setOriginalTopic(topic);
77   -
78   - // 检查是否为响应主题
79   - boolean isReply = topic.endsWith(IotTopicConstants.REPLY_SUFFIX);
80   - info.setReply(isReply);
81   -
82   - // 移除响应后缀,便于后续解析
83   - String normalizedTopic = isReply ? topic.substring(0, topic.length() - IotTopicConstants.REPLY_SUFFIX.length())
84   - : topic;
85   -
86   - // 解析系统主题
87   - if (normalizedTopic.startsWith(IotTopicConstants.SYS_TOPIC_PREFIX)) {
88   - return parseSystemTopic(info, normalizedTopic);
89   - }
90   -
91   - // 解析自定义主题
92   - return parseCustomTopic(info, normalizedTopic);
93   - }
94   -
95   - /**
96   - * 解析系统主题
97   - * 格式:/sys/{productKey}/{deviceName}/thing/service/{identifier}
98   - * 或:/sys/{productKey}/{deviceName}/thing/event/{identifier}/post
99   - */
100   - private static TopicInfo parseSystemTopic(TopicInfo info, String topic) {
101   - String[] parts = topic.split("/");
102   - if (parts.length < 6) {
103   - return null;
104   - }
105   -
106   - // 解析产品Key和设备名称
107   - info.setProductKey(parts[2]);
108   - info.setDeviceName(parts[3]);
109   -
110   - // 判断消息方向:包含 /post 通常是上行,其他是下行
111   - info.setDirection(topic.contains("/post") || topic.contains("/reply") ? IotMessageDirectionEnum.UPSTREAM
112   - : IotMessageDirectionEnum.DOWNSTREAM);
113   -
114   - // 解析具体的消息类型
115   - if (topic.contains("/thing/service/")) {
116   - return parseServiceTopic(info, topic, parts);
117   - } else if (topic.contains("/thing/event/")) {
118   - return parseEventTopic(info, topic, parts);
119   - }
120   -
121   - return null;
122   - }
123   -
124   - /**
125   - * 解析服务相关主题
126   - */
127   - private static TopicInfo parseServiceTopic(TopicInfo info, String topic, String[] parts) {
128   - // 查找 service 关键字的位置
129   - int serviceIndex = -1;
130   - for (int i = 0; i < parts.length; i++) {
131   - if ("service".equals(parts[i])) {
132   - serviceIndex = i;
133   - break;
134   - }
135   - }
136   -
137   - if (serviceIndex == -1 || serviceIndex + 1 >= parts.length) {
138   - return null;
139   - }
140   -
141   - String serviceType = parts[serviceIndex + 1];
142   -
143   - // 根据服务类型确定消息类型
144   - switch (serviceType) {
145   - case "property":
146   - if (serviceIndex + 2 < parts.length) {
147   - String operation = parts[serviceIndex + 2];
148   - if ("set".equals(operation)) {
149   - info.setMessageType(IotMessageTypeEnum.PROPERTY_SET);
150   - } else if ("get".equals(operation)) {
151   - info.setMessageType(IotMessageTypeEnum.PROPERTY_GET);
152   - }
153   - }
154   - break;
155   - case "config":
156   - if (serviceIndex + 2 < parts.length && "set".equals(parts[serviceIndex + 2])) {
157   - info.setMessageType(IotMessageTypeEnum.CONFIG_SET);
158   - }
159   - break;
160   - case "ota":
161   - if (serviceIndex + 2 < parts.length && "upgrade".equals(parts[serviceIndex + 2])) {
162   - info.setMessageType(IotMessageTypeEnum.OTA_UPGRADE);
163   - }
164   - break;
165   - default:
166   - // 自定义服务
167   - info.setMessageType(IotMessageTypeEnum.SERVICE_INVOKE);
168   - info.setServiceIdentifier(serviceType);
169   - break;
170   - }
171   -
172   - return info;
173   - }
174   -
175   - /**
176   - * 解析事件相关主题
177   - */
178   - private static TopicInfo parseEventTopic(TopicInfo info, String topic, String[] parts) {
179   - // 查找 event 关键字的位置
180   - int eventIndex = -1;
181   - for (int i = 0; i < parts.length; i++) {
182   - if ("event".equals(parts[i])) {
183   - eventIndex = i;
184   - break;
185   - }
186   - }
187   -
188   - if (eventIndex == -1 || eventIndex + 1 >= parts.length) {
189   - return null;
190   - }
191   -
192   - String eventType = parts[eventIndex + 1];
193   -
194   - if ("property".equals(eventType) && eventIndex + 2 < parts.length && "post".equals(parts[eventIndex + 2])) {
195   - info.setMessageType(IotMessageTypeEnum.PROPERTY_POST);
196   - } else {
197   - // 自定义事件
198   - info.setMessageType(IotMessageTypeEnum.EVENT_POST);
199   - info.setEventIdentifier(eventType);
200   - }
201   -
202   - return info;
203   - }
204   -
205   - /**
206   - * 解析自定义主题
207   - * 这里可以根据实际需求扩展自定义主题的解析逻辑
208   - */
209   - private static TopicInfo parseCustomTopic(TopicInfo info, String topic) {
210   - // TODO: 根据业务需要实现自定义主题解析逻辑
211   - return info;
212   - }
213   -
214   - /**
215   - * 检查主题是否为有效的系统主题
216   - *
217   - * @param topic 主题
218   - * @return 如果是有效的系统主题返回 true,否则返回 false
219   - */
220   - public static boolean isValidSystemTopic(String topic) {
221   - TopicInfo info = parse(topic);
222   - return info != null &&
223   - StrUtil.isNotBlank(info.getProductKey()) &&
224   - StrUtil.isNotBlank(info.getDeviceName()) &&
225   - info.getMessageType() != null;
226   - }
227   -
228   - /**
229   - * 检查主题是否为响应主题
230   - *
231   - * @param topic 主题
232   - * @return 如果是响应主题返回 true,否则返回 false
233   - */
234   - public static boolean isReplyTopic(String topic) {
235   - return topic != null && topic.endsWith(IotTopicConstants.REPLY_SUFFIX);
236   - }
237   -}
238 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/main/java/cn/iocoder/yudao/module/iot/protocol/util/IotTopicUtils.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.util;
2   -
3   -import cn.hutool.core.util.StrUtil;
4   -import cn.iocoder.yudao.module.iot.protocol.constants.IotTopicConstants;
5   -
6   -/**
7   - * IoT 主题工具类
8   - * <p>
9   - * 用于构建和解析设备主题
10   - *
11   - * @author haohao
12   - */
13   -public class IotTopicUtils {
14   -
15   - /**
16   - * 构建设备服务调用主题
17   - *
18   - * @param productKey 产品Key
19   - * @param deviceName 设备名称
20   - * @param serviceIdentifier 服务标识符
21   - * @return 完整的主题路径
22   - */
23   - public static String buildServiceTopic(String productKey, String deviceName, String serviceIdentifier) {
24   - return buildDeviceBaseTopic(productKey, deviceName) +
25   - IotTopicConstants.SERVICE_TOPIC_PREFIX + serviceIdentifier;
26   - }
27   -
28   - /**
29   - * 构建设备属性设置主题
30   - *
31   - * @param productKey 产品Key
32   - * @param deviceName 设备名称
33   - * @return 完整的主题路径
34   - */
35   - public static String buildPropertySetTopic(String productKey, String deviceName) {
36   - return buildDeviceBaseTopic(productKey, deviceName) + IotTopicConstants.PROPERTY_SET_TOPIC;
37   - }
38   -
39   - /**
40   - * 构建设备属性获取主题
41   - *
42   - * @param productKey 产品Key
43   - * @param deviceName 设备名称
44   - * @return 完整的主题路径
45   - */
46   - public static String buildPropertyGetTopic(String productKey, String deviceName) {
47   - return buildDeviceBaseTopic(productKey, deviceName) + IotTopicConstants.PROPERTY_GET_TOPIC;
48   - }
49   -
50   - /**
51   - * 构建设备配置设置主题
52   - *
53   - * @param productKey 产品Key
54   - * @param deviceName 设备名称
55   - * @return 完整的主题路径
56   - */
57   - public static String buildConfigSetTopic(String productKey, String deviceName) {
58   - return buildDeviceBaseTopic(productKey, deviceName) + IotTopicConstants.CONFIG_SET_TOPIC;
59   - }
60   -
61   - /**
62   - * 构建设备 OTA 升级主题
63   - *
64   - * @param productKey 产品Key
65   - * @param deviceName 设备名称
66   - * @return 完整的主题路径
67   - */
68   - public static String buildOtaUpgradeTopic(String productKey, String deviceName) {
69   - return buildDeviceBaseTopic(productKey, deviceName) + IotTopicConstants.OTA_UPGRADE_TOPIC;
70   - }
71   -
72   - /**
73   - * 构建设备属性上报主题
74   - *
75   - * @param productKey 产品Key
76   - * @param deviceName 设备名称
77   - * @return 完整的主题路径
78   - */
79   - public static String buildPropertyPostTopic(String productKey, String deviceName) {
80   - return buildDeviceBaseTopic(productKey, deviceName) + IotTopicConstants.PROPERTY_POST_TOPIC;
81   - }
82   -
83   - /**
84   - * 构建设备事件上报主题
85   - *
86   - * @param productKey 产品Key
87   - * @param deviceName 设备名称
88   - * @param eventIdentifier 事件标识符
89   - * @return 完整的主题路径
90   - */
91   - public static String buildEventPostTopic(String productKey, String deviceName, String eventIdentifier) {
92   - return buildDeviceBaseTopic(productKey, deviceName) +
93   - IotTopicConstants.EVENT_POST_TOPIC_PREFIX + eventIdentifier + IotTopicConstants.EVENT_POST_TOPIC_SUFFIX;
94   - }
95   -
96   - /**
97   - * 获取响应主题
98   - *
99   - * @param requestTopic 请求主题
100   - * @return 响应主题
101   - */
102   - public static String getReplyTopic(String requestTopic) {
103   - return requestTopic + IotTopicConstants.REPLY_SUFFIX;
104   - }
105   -
106   - /**
107   - * 构建设备基础主题
108   - * 格式: /sys/${productKey}/${deviceName}
109   - *
110   - * @param productKey 产品Key
111   - * @param deviceName 设备名称
112   - * @return 设备基础主题
113   - */
114   - public static String buildDeviceBaseTopic(String productKey, String deviceName) {
115   - return IotTopicConstants.SYS_TOPIC_PREFIX + productKey + "/" + deviceName;
116   - }
117   -
118   - /**
119   - * 从主题中解析产品Key
120   - * 格式: /sys/${productKey}/${deviceName}/...
121   - *
122   - * @param topic 主题
123   - * @return 产品Key,如果无法解析则返回null
124   - */
125   - public static String parseProductKeyFromTopic(String topic) {
126   - if (StrUtil.isBlank(topic) || !topic.startsWith(IotTopicConstants.SYS_TOPIC_PREFIX)) {
127   - return null;
128   - }
129   -
130   - String[] parts = topic.split("/");
131   - if (parts.length < 4) {
132   - return null;
133   - }
134   -
135   - return parts[2];
136   - }
137   -
138   - /**
139   - * 从主题中解析设备名称
140   - * 格式: /sys/${productKey}/${deviceName}/...
141   - *
142   - * @param topic 主题
143   - * @return 设备名称,如果无法解析则返回null
144   - */
145   - public static String parseDeviceNameFromTopic(String topic) {
146   - if (StrUtil.isBlank(topic) || !topic.startsWith(IotTopicConstants.SYS_TOPIC_PREFIX)) {
147   - return null;
148   - }
149   -
150   - String[] parts = topic.split("/");
151   - if (parts.length < 4) {
152   - return null;
153   - }
154   -
155   - return parts[3];
156   - }
157   -
158   - /**
159   - * 从主题中解析方法名
160   - * 例如:从 /sys/pk/dn/thing/service/property/set 解析出 property.set
161   - *
162   - * @param topic 主题
163   - * @return 方法名,如果无法解析则返回null
164   - */
165   - public static String parseMethodFromTopic(String topic) {
166   - if (StrUtil.isBlank(topic) || !topic.startsWith(IotTopicConstants.SYS_TOPIC_PREFIX)) {
167   - return null;
168   - }
169   -
170   - // 服务调用主题
171   - if (topic.contains("/thing/service/")) {
172   - String servicePart = topic.substring(topic.indexOf("/thing/service/") + "/thing/service/".length());
173   - return servicePart.replace("/", ".");
174   - }
175   -
176   - // 事件上报主题
177   - if (topic.contains("/thing/event/")) {
178   - String eventPart = topic.substring(topic.indexOf("/thing/event/") + "/thing/event/".length());
179   - return "event." + eventPart.replace("/", ".");
180   - }
181   -
182   - return null;
183   - }
184   -}
185 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports deleted
1   -cn.iocoder.yudao.module.iot.protocol.config.IotProtocolAutoConfiguration
2 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/config/IotProtocolAutoConfigurationTest.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.config;
2   -
3   -import cn.iocoder.yudao.module.iot.protocol.convert.IotProtocolConverter;
4   -import cn.iocoder.yudao.module.iot.protocol.enums.IotProtocolTypeEnum;
5   -import cn.iocoder.yudao.module.iot.protocol.message.IotMessageParser;
6   -import cn.iocoder.yudao.module.iot.protocol.message.impl.IotHttpMessageParser;
7   -import cn.iocoder.yudao.module.iot.protocol.message.impl.IotMqttMessageParser;
8   -import org.junit.jupiter.api.BeforeEach;
9   -import org.junit.jupiter.api.Test;
10   -
11   -import static org.junit.jupiter.api.Assertions.*;
12   -
13   -/**
14   - * {@link IotProtocolAutoConfiguration} 单元测试
15   - *
16   - * @author haohao
17   - */
18   -class IotProtocolAutoConfigurationTest {
19   -
20   - private IotProtocolAutoConfiguration configuration;
21   -
22   - @BeforeEach
23   - void setUp() {
24   - configuration = new IotProtocolAutoConfiguration();
25   - }
26   -
27   - @Test
28   - void testIotMqttMessageParser() {
29   - // 测试 MQTT 协议解析器 Bean 创建
30   - IotMessageParser parser = configuration.iotMqttMessageParser();
31   -
32   - assertNotNull(parser);
33   - assertInstanceOf(IotMqttMessageParser.class, parser);
34   - }
35   -
36   - @Test
37   - void testIotHttpMessageParser() {
38   - // 测试 HTTP 协议解析器 Bean 创建
39   - IotMessageParser parser = configuration.iotHttpMessageParser();
40   -
41   - assertNotNull(parser);
42   - assertInstanceOf(IotHttpMessageParser.class, parser);
43   - }
44   -
45   - @Test
46   - void testIotProtocolConverter() {
47   - // 创建解析器实例
48   - IotMessageParser mqttParser = configuration.iotMqttMessageParser();
49   - IotMessageParser httpParser = configuration.iotHttpMessageParser();
50   -
51   - // 测试协议转换器 Bean 创建
52   - IotProtocolConverter converter = configuration.iotProtocolConverter(mqttParser, httpParser);
53   -
54   - assertNotNull(converter);
55   -
56   - // 验证支持的协议
57   - assertTrue(converter.supportsProtocol(IotProtocolTypeEnum.MQTT.getCode()));
58   - assertTrue(converter.supportsProtocol(IotProtocolTypeEnum.HTTP.getCode()));
59   -
60   - // 验证支持的协议数量
61   - String[] supportedProtocols = converter.getSupportedProtocols();
62   - assertEquals(2, supportedProtocols.length);
63   - }
64   -
65   - @Test
66   - void testBeanNameConstants() {
67   - // 测试 Bean 名称常量定义
68   - assertEquals("iotMqttMessageParser", IotProtocolAutoConfiguration.IOT_MQTT_MESSAGE_PARSER_BEAN_NAME);
69   - assertEquals("iotHttpMessageParser", IotProtocolAutoConfiguration.IOT_HTTP_MESSAGE_PARSER_BEAN_NAME);
70   - }
71   -}
72 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/example/AliyunHttpProtocolExample.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.example;
2   -
3   -import cn.hutool.json.JSONObject;
4   -import cn.iocoder.yudao.module.iot.protocol.util.IotHttpTopicUtils;
5   -
6   -/**
7   - * 阿里云IoT平台HTTPS协议示例
8   - * <p>
9   - * 参考阿里云IoT平台HTTPS连接通信标准,演示设备认证和数据上报的完整流程
10   - *
11   - * @author haohao
12   - */
13   -public class AliyunHttpProtocolExample {
14   -
15   - public static void main(String[] args) {
16   - System.out.println("=== 阿里云IoT平台HTTPS协议演示 ===\n");
17   -
18   - // 演示设备认证
19   - demonstrateDeviceAuth();
20   -
21   - // 演示数据上报
22   - demonstrateDataUpload();
23   -
24   - // 演示路径构建
25   - demonstratePathBuilding();
26   - }
27   -
28   - /**
29   - * 演示设备认证流程
30   - */
31   - private static void demonstrateDeviceAuth() {
32   - System.out.println("1. 设备认证流程:");
33   - System.out.println("认证路径: " + IotHttpTopicUtils.buildAuthPath());
34   -
35   - // 构建认证请求消息
36   - JSONObject authRequest = new JSONObject();
37   - authRequest.set("productKey", "a1GFjLP****");
38   - authRequest.set("deviceName", "device123");
39   - authRequest.set("clientId", "device123_001");
40   - authRequest.set("timestamp", String.valueOf(System.currentTimeMillis()));
41   - authRequest.set("sign", "4870141D4067227128CBB4377906C3731CAC221C");
42   - authRequest.set("signmethod", "hmacsha1");
43   - authRequest.set("version", "default");
44   -
45   - System.out.println("认证请求消息:");
46   - System.out.println(authRequest.toString());
47   -
48   - // 模拟认证响应
49   - JSONObject authResponse = new JSONObject();
50   - authResponse.set("code", 0);
51   - authResponse.set("message", "success");
52   -
53   - JSONObject info = new JSONObject();
54   - info.set("token", "6944e5bfb92e4d4ea3918d1eda39****");
55   - authResponse.set("info", info);
56   -
57   - System.out.println("认证响应:");
58   - System.out.println(authResponse.toString());
59   - System.out.println();
60   - }
61   -
62   - /**
63   - * 演示数据上报流程
64   - */
65   - private static void demonstrateDataUpload() {
66   - System.out.println("2. 数据上报流程:");
67   -
68   - String productKey = "a1GFjLP****";
69   - String deviceName = "device123";
70   -
71   - // 属性上报
72   - String propertyPostPath = IotHttpTopicUtils.buildPropertyPostPath(productKey, deviceName);
73   - System.out.println("属性上报路径: " + propertyPostPath);
74   -
75   - // Alink格式的属性上报消息
76   - JSONObject propertyMessage = new JSONObject();
77   - propertyMessage.set("id", "123456");
78   - propertyMessage.set("version", "1.0");
79   - propertyMessage.set("method", "thing.event.property.post");
80   -
81   - JSONObject propertyParams = new JSONObject();
82   - JSONObject properties = new JSONObject();
83   - properties.set("temperature", 25.6);
84   - properties.set("humidity", 60.3);
85   - propertyParams.set("properties", properties);
86   - propertyMessage.set("params", propertyParams);
87   -
88   - System.out.println("属性上报消息:");
89   - System.out.println(propertyMessage.toString());
90   -
91   - // 事件上报
92   - String eventPostPath = IotHttpTopicUtils.buildEventPostPath(productKey, deviceName, "temperatureAlert");
93   - System.out.println("\n事件上报路径: " + eventPostPath);
94   -
95   - JSONObject eventMessage = new JSONObject();
96   - eventMessage.set("id", "123457");
97   - eventMessage.set("version", "1.0");
98   - eventMessage.set("method", "thing.event.temperatureAlert.post");
99   -
100   - JSONObject eventParams = new JSONObject();
101   - eventParams.set("value", new JSONObject().set("alertLevel", "high").set("currentTemp", 45.2));
102   - eventParams.set("time", System.currentTimeMillis());
103   - eventMessage.set("params", eventParams);
104   -
105   - System.out.println("事件上报消息:");
106   - System.out.println(eventMessage.toString());
107   -
108   - // 模拟数据上报响应
109   - JSONObject uploadResponse = new JSONObject();
110   - uploadResponse.set("code", 0);
111   - uploadResponse.set("message", "success");
112   -
113   - JSONObject responseInfo = new JSONObject();
114   - responseInfo.set("messageId", 892687470447040L);
115   - uploadResponse.set("info", responseInfo);
116   -
117   - System.out.println("\n数据上报响应:");
118   - System.out.println(uploadResponse.toString());
119   - System.out.println();
120   - }
121   -
122   - /**
123   - * 演示路径构建功能
124   - */
125   - private static void demonstratePathBuilding() {
126   - System.out.println("3. 路径构建功能:");
127   -
128   - String productKey = "smartProduct";
129   - String deviceName = "sensor001";
130   -
131   - // 系统主题路径
132   - System.out.println("系统主题路径:");
133   - System.out.println(" 属性设置: " + IotHttpTopicUtils.buildPropertySetPath(productKey, deviceName));
134   - System.out.println(" 属性获取: " + IotHttpTopicUtils.buildPropertyGetPath(productKey, deviceName));
135   - System.out.println(" 属性上报: " + IotHttpTopicUtils.buildPropertyPostPath(productKey, deviceName));
136   - System.out.println(" 事件上报: " + IotHttpTopicUtils.buildEventPostPath(productKey, deviceName, "alarm"));
137   - System.out.println(" 服务调用: " + IotHttpTopicUtils.buildServiceInvokePath(productKey, deviceName, "reboot"));
138   -
139   - // 自定义主题路径
140   - System.out.println("\n自定义主题路径:");
141   - System.out.println(" 用户主题: " + IotHttpTopicUtils.buildCustomTopicPath(productKey, deviceName, "user/get"));
142   -
143   - // 响应路径
144   - String requestPath = IotHttpTopicUtils.buildPropertySetPath(productKey, deviceName);
145   - String replyPath = IotHttpTopicUtils.buildReplyPath(requestPath);
146   - System.out.println("\n响应路径:");
147   - System.out.println(" 请求路径: " + requestPath);
148   - System.out.println(" 响应路径: " + replyPath);
149   -
150   - // 路径解析
151   - System.out.println("\n路径解析:");
152   - String testPath = "/topic/sys/smartProduct/sensor001/thing/service/property/set";
153   - String actualTopic = IotHttpTopicUtils.extractActualTopic(testPath);
154   - System.out.println(" HTTP路径: " + testPath);
155   - System.out.println(" 实际主题: " + actualTopic);
156   - System.out.println(" 产品Key: " + IotHttpTopicUtils.parseProductKeyFromTopic(actualTopic));
157   - System.out.println(" 设备名称: " + IotHttpTopicUtils.parseDeviceNameFromTopic(actualTopic));
158   - System.out.println(" 是否为系统主题: " + IotHttpTopicUtils.isSystemTopic(actualTopic));
159   -
160   - // 路径类型检查
161   - System.out.println("\n路径类型检查:");
162   - System.out.println(" 认证路径检查: " + IotHttpTopicUtils.isAuthPath("/auth"));
163   - System.out.println(" 数据路径检查: " + IotHttpTopicUtils.isTopicPath("/topic/test"));
164   - System.out.println(" 有效路径检查: " + IotHttpTopicUtils.isValidHttpPath("/topic/sys/test/device/property"));
165   - }
166   -}
167 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotHttpMessageParserTest.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.message.impl;
2   -
3   -import cn.hutool.json.JSONObject;
4   -import cn.iocoder.yudao.module.iot.protocol.message.IotMqttMessage;
5   -import cn.iocoder.yudao.module.iot.protocol.message.IotStandardResponse;
6   -import org.junit.jupiter.api.BeforeEach;
7   -import org.junit.jupiter.api.Test;
8   -
9   -import java.nio.charset.StandardCharsets;
10   -import java.util.HashMap;
11   -import java.util.Map;
12   -
13   -import static org.junit.jupiter.api.Assertions.*;
14   -
15   -/**
16   - * {@link IotHttpMessageParser} 单元测试
17   - * <p>
18   - * 测试阿里云IoT平台HTTPS协议标准的消息解析功能
19   - *
20   - * @author haohao
21   - */
22   -class IotHttpMessageParserTest {
23   -
24   - private IotHttpMessageParser parser;
25   -
26   - @BeforeEach
27   - void setUp() {
28   - parser = new IotHttpMessageParser();
29   - }
30   -
31   - @Test
32   - void testCanHandle() {
33   - // 测试能处理的路径
34   - assertTrue(parser.canHandle("/auth"));
35   - assertTrue(parser.canHandle("/topic/sys/test/device1/thing/service/property/set"));
36   - assertTrue(parser.canHandle("/topic/test/device1/user/get"));
37   -
38   - // 测试不能处理的路径
39   - assertFalse(parser.canHandle("/sys/test/device1/thing/service/property/set"));
40   - assertFalse(parser.canHandle("/unknown/path"));
41   - assertFalse(parser.canHandle(null));
42   - assertFalse(parser.canHandle(""));
43   - }
44   -
45   - @Test
46   - void testParseAuthMessage() {
47   - // 构建认证消息
48   - JSONObject authMessage = new JSONObject();
49   - authMessage.set("productKey", "a1GFjLP****");
50   - authMessage.set("deviceName", "device123");
51   - authMessage.set("clientId", "device123_001");
52   - authMessage.set("timestamp", "1501668289957");
53   - authMessage.set("sign", "4870141D4067227128CBB4377906C3731CAC221C");
54   - authMessage.set("signmethod", "hmacsha1");
55   - authMessage.set("version", "default");
56   -
57   - String topic = "/auth";
58   - byte[] payload = authMessage.toString().getBytes(StandardCharsets.UTF_8);
59   -
60   - // 解析消息
61   - IotMqttMessage result = parser.parse(topic, payload);
62   -
63   - // 验证结果
64   - assertNotNull(result);
65   - assertNotNull(result.getId());
66   - assertEquals("device.auth", result.getMethod());
67   - assertEquals("default", result.getVersion());
68   - assertNotNull(result.getParams());
69   -
70   - Map<String, Object> params = result.getParams();
71   - assertEquals("a1GFjLP****", params.get("productKey"));
72   - assertEquals("device123", params.get("deviceName"));
73   - assertEquals("device123_001", params.get("clientId"));
74   - assertEquals("1501668289957", params.get("timestamp"));
75   - assertEquals("4870141D4067227128CBB4377906C3731CAC221C", params.get("sign"));
76   - assertEquals("hmacsha1", params.get("signmethod"));
77   - }
78   -
79   - @Test
80   - void testParseAuthMessageWithMissingFields() {
81   - // 构建缺少必需字段的认证消息
82   - JSONObject authMessage = new JSONObject();
83   - authMessage.set("productKey", "a1GFjLP****");
84   - authMessage.set("deviceName", "device123");
85   - // 缺少 clientId 和 sign
86   -
87   - String topic = "/auth";
88   - byte[] payload = authMessage.toString().getBytes(StandardCharsets.UTF_8);
89   -
90   - // 解析消息
91   - IotMqttMessage result = parser.parse(topic, payload);
92   -
93   - // 验证结果
94   - assertNull(result);
95   - }
96   -
97   - @Test
98   - void testParseJsonDataMessage() {
99   - // 构建JSON格式的数据消息
100   - JSONObject dataMessage = new JSONObject();
101   - dataMessage.set("id", "123456");
102   - dataMessage.set("version", "1.0");
103   - dataMessage.set("method", "thing.event.property.post");
104   -
105   - JSONObject params = new JSONObject();
106   - JSONObject properties = new JSONObject();
107   - properties.set("temperature", 25.6);
108   - properties.set("humidity", 60.3);
109   - params.set("properties", properties);
110   - dataMessage.set("params", params);
111   -
112   - String topic = "/topic/sys/a1GFjLP****/device123/thing/event/property/post";
113   - byte[] payload = dataMessage.toString().getBytes(StandardCharsets.UTF_8);
114   -
115   - // 解析消息
116   - IotMqttMessage result = parser.parse(topic, payload);
117   -
118   - // 验证结果
119   - assertNotNull(result);
120   - assertEquals("123456", result.getId());
121   - assertEquals("thing.event.property.post", result.getMethod());
122   - assertEquals("1.0", result.getVersion());
123   - assertNotNull(result.getParams());
124   - assertNotNull(result.getParams().get("properties"));
125   - }
126   -
127   - @Test
128   - void testParseRawDataMessage() {
129   - // 原始数据消息
130   - String rawData = "temperature:25.6,humidity:60.3";
131   - String topic = "/topic/sys/a1GFjLP****/device123/thing/event/property/post";
132   - byte[] payload = rawData.getBytes(StandardCharsets.UTF_8);
133   -
134   - // 解析消息
135   - IotMqttMessage result = parser.parse(topic, payload);
136   -
137   - // 验证结果
138   - assertNotNull(result);
139   - assertNotNull(result.getId());
140   - assertEquals("thing.event.property.post", result.getMethod());
141   - assertEquals("1.0", result.getVersion());
142   - assertNotNull(result.getParams());
143   - assertEquals(rawData, result.getParams().get("data"));
144   - }
145   -
146   - @Test
147   - void testInferMethodFromTopic() {
148   - // 测试系统主题方法推断
149   - testInferMethod("/sys/test/device/thing/service/property/set", "thing.service.property.set");
150   - testInferMethod("/sys/test/device/thing/service/property/get", "thing.service.property.get");
151   - testInferMethod("/sys/test/device/thing/event/property/post", "thing.event.property.post");
152   - testInferMethod("/sys/test/device/thing/event/alarm/post", "thing.event.alarm.post");
153   - testInferMethod("/sys/test/device/thing/service/reboot", "thing.service.reboot");
154   -
155   - // 测试自定义主题
156   - testInferMethod("/test/device/user/get", "custom.message");
157   - }
158   -
159   - private void testInferMethod(String actualTopic, String expectedMethod) {
160   - String topic = "/topic" + actualTopic;
161   - String rawData = "test data";
162   - byte[] payload = rawData.getBytes(StandardCharsets.UTF_8);
163   -
164   - IotMqttMessage result = parser.parse(topic, payload);
165   - assertNotNull(result);
166   - assertEquals(expectedMethod, result.getMethod());
167   - }
168   -
169   - @Test
170   - void testFormatAuthResponse() {
171   - // 创建认证成功响应
172   - Map<String, Object> data = new HashMap<>();
173   - data.put("token", "6944e5bfb92e4d4ea3918d1eda39****");
174   -
175   - IotStandardResponse response = IotStandardResponse.success("auth123", "device.auth", data);
176   -
177   - // 格式化响应
178   - byte[] result = parser.formatResponse(response);
179   -
180   - // 验证结果
181   - assertNotNull(result);
182   - assertTrue(result.length > 0);
183   -
184   - String responseStr = new String(result, StandardCharsets.UTF_8);
185   - JSONObject responseJson = new JSONObject(responseStr);
186   -
187   - assertEquals(200, responseJson.getInt("code"));
188   - assertEquals("success", responseJson.getStr("message"));
189   - assertNotNull(responseJson.get("info"));
190   -
191   - JSONObject info = responseJson.getJSONObject("info");
192   - assertEquals("6944e5bfb92e4d4ea3918d1eda39****", info.getStr("token"));
193   - }
194   -
195   - @Test
196   - void testFormatDataResponse() {
197   - // 创建数据上报响应
198   - IotStandardResponse response = IotStandardResponse.success("123456", "thing.event.property.post", null);
199   -
200   - // 格式化响应
201   - byte[] result = parser.formatResponse(response);
202   -
203   - // 验证结果
204   - assertNotNull(result);
205   - assertTrue(result.length > 0);
206   -
207   - String responseStr = new String(result, StandardCharsets.UTF_8);
208   - JSONObject responseJson = new JSONObject(responseStr);
209   -
210   - assertEquals(200, responseJson.getInt("code"));
211   - assertEquals("success", responseJson.getStr("message"));
212   - assertNotNull(responseJson.get("info"));
213   -
214   - JSONObject info = responseJson.getJSONObject("info");
215   - assertEquals("123456", info.getStr("messageId"));
216   - }
217   -
218   - @Test
219   - void testParseInvalidMessage() {
220   - String topic = "/topic/sys/test/device/thing/service/property/set";
221   -
222   - // 测试空消息
223   - assertNull(parser.parse(topic, null));
224   - assertNull(parser.parse(topic, new byte[0]));
225   -
226   - // 测试不支持的路径
227   - byte[] validPayload = "test data".getBytes(StandardCharsets.UTF_8);
228   - assertNull(parser.parse("/unknown/path", validPayload));
229   - }
230   -
231   - @Test
232   - void testParseDeviceKey() {
233   - // 测试有效的设备标识
234   - String[] result1 = IotHttpMessageParser.parseDeviceKey("productKey/deviceName");
235   - assertNotNull(result1);
236   - assertEquals(2, result1.length);
237   - assertEquals("productKey", result1[0]);
238   - assertEquals("deviceName", result1[1]);
239   -
240   - // 测试无效的设备标识
241   - assertNull(IotHttpMessageParser.parseDeviceKey(null));
242   - assertNull(IotHttpMessageParser.parseDeviceKey(""));
243   - assertNull(IotHttpMessageParser.parseDeviceKey("invalid"));
244   - assertNull(IotHttpMessageParser.parseDeviceKey("product/device/extra"));
245   - }
246   -
247   - @Test
248   - void testBuildDeviceKey() {
249   - // 测试构建设备标识
250   - assertEquals("productKey/deviceName",
251   - IotHttpMessageParser.buildDeviceKey("productKey", "deviceName"));
252   -
253   - // 测试无效参数
254   - assertNull(IotHttpMessageParser.buildDeviceKey(null, "deviceName"));
255   - assertNull(IotHttpMessageParser.buildDeviceKey("productKey", null));
256   - assertNull(IotHttpMessageParser.buildDeviceKey("", "deviceName"));
257   - assertNull(IotHttpMessageParser.buildDeviceKey("productKey", ""));
258   - }
259   -}
260 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/message/impl/IotMqttMessageParserTest.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.message.impl;
2   -
3   -import cn.hutool.json.JSONObject;
4   -import cn.iocoder.yudao.module.iot.protocol.message.IotMqttMessage;
5   -import cn.iocoder.yudao.module.iot.protocol.message.IotStandardResponse;
6   -import org.junit.jupiter.api.BeforeEach;
7   -import org.junit.jupiter.api.Test;
8   -
9   -import java.nio.charset.StandardCharsets;
10   -import java.util.HashMap;
11   -import java.util.Map;
12   -
13   -import static org.junit.jupiter.api.Assertions.*;
14   -
15   -/**
16   - * IoT MQTT 消息解析器测试类
17   - *
18   - * @author haohao
19   - */
20   -class IotMqttMessageParserTest {
21   -
22   - private IotMqttMessageParser parser;
23   -
24   - @BeforeEach
25   - void setUp() {
26   - parser = new IotMqttMessageParser();
27   - }
28   -
29   - @Test
30   - void testParseValidJsonMessage() {
31   - // 构建有效的 JSON 消息
32   - JSONObject message = new JSONObject();
33   - message.set("id", "123456");
34   - message.set("version", "1.0");
35   - message.set("method", "thing.service.property.set");
36   -
37   - Map<String, Object> params = new HashMap<>();
38   - params.put("temperature", 25.5);
39   - params.put("humidity", 60.0);
40   - message.set("params", params);
41   -
42   - String topic = "/sys/productKey/deviceName/thing/service/property/set";
43   - byte[] payload = message.toString().getBytes(StandardCharsets.UTF_8);
44   -
45   - // 解析消息
46   - IotMqttMessage result = parser.parse(topic, payload);
47   -
48   - // 验证结果
49   - assertNotNull(result);
50   - assertEquals("123456", result.getId());
51   - assertEquals("1.0", result.getVersion());
52   - assertEquals("thing.service.property.set", result.getMethod());
53   - assertNotNull(result.getParams());
54   - assertEquals(25.5, ((Number) result.getParams().get("temperature")).doubleValue());
55   - assertEquals(60.0, ((Number) result.getParams().get("humidity")).doubleValue());
56   - }
57   -
58   - @Test
59   - void testParseMessageWithoutMethod() {
60   - // 构建没有 method 字段的消息,应该从 topic 中解析
61   - JSONObject message = new JSONObject();
62   - message.set("id", "789012");
63   - message.set("version", "1.0");
64   -
65   - Map<String, Object> params = new HashMap<>();
66   - params.put("voltage", 3.3);
67   - message.set("params", params);
68   -
69   - String topic = "/sys/productKey/deviceName/thing/service/property/set";
70   - byte[] payload = message.toString().getBytes(StandardCharsets.UTF_8);
71   -
72   - // 解析消息
73   - IotMqttMessage result = parser.parse(topic, payload);
74   -
75   - // 验证结果
76   - assertNotNull(result);
77   - assertEquals("789012", result.getId());
78   - assertEquals("1.0", result.getVersion());
79   - assertNotNull(result.getMethod()); // 应该从 topic 中解析出方法
80   - assertNotNull(result.getParams());
81   - assertEquals(3.3, ((Number) result.getParams().get("voltage")).doubleValue());
82   - }
83   -
84   - @Test
85   - void testParseInvalidJsonMessage() {
86   - String topic = "/sys/productKey/deviceName/thing/service/property/set";
87   - byte[] payload = "invalid json".getBytes(StandardCharsets.UTF_8);
88   -
89   - // 解析消息
90   - IotMqttMessage result = parser.parse(topic, payload);
91   -
92   - // 验证结果
93   - assertNull(result);
94   - }
95   -
96   - @Test
97   - void testParseEmptyPayload() {
98   - String topic = "/sys/productKey/deviceName/thing/service/property/set";
99   -
100   - // 测试 null payload
101   - IotMqttMessage result1 = parser.parse(topic, null);
102   - assertNull(result1);
103   -
104   - // 测试空 payload
105   - IotMqttMessage result2 = parser.parse(topic, new byte[0]);
106   - assertNull(result2);
107   - }
108   -
109   - @Test
110   - void testFormatResponse() {
111   - // 创建标准响应
112   - IotStandardResponse response = IotStandardResponse.success("123456", "property.set", null);
113   -
114   - // 格式化响应
115   - byte[] result = parser.formatResponse(response);
116   -
117   - // 验证结果
118   - assertNotNull(result);
119   - assertTrue(result.length > 0);
120   -
121   - // 验证 JSON 格式
122   - String jsonString = new String(result, StandardCharsets.UTF_8);
123   - assertTrue(jsonString.contains("123456"));
124   - assertTrue(jsonString.contains("property.set"));
125   - }
126   -
127   - @Test
128   - void testCanHandle() {
129   - // 测试支持的主题格式
130   - assertTrue(parser.canHandle("/sys/productKey/deviceName/thing/service/property/set"));
131   - assertTrue(parser.canHandle("/mqtt/productKey/deviceName/property/set"));
132   - assertTrue(parser.canHandle("/device/productKey/deviceName/data"));
133   -
134   - // 测试不支持的主题格式
135   - assertFalse(parser.canHandle("/http/device/productKey/deviceName/property/set"));
136   - assertFalse(parser.canHandle("/unknown/topic"));
137   - assertFalse(parser.canHandle(null));
138   - assertFalse(parser.canHandle(""));
139   - }
140   -
141   - @Test
142   - void testParseMqttTopicFormat() {
143   - // 测试新的 MQTT 主题格式
144   - JSONObject message = new JSONObject();
145   - message.set("id", "mqtt001");
146   - message.set("version", "1.0");
147   - message.set("method", "device.property.report");
148   -
149   - Map<String, Object> params = new HashMap<>();
150   - params.put("signal", 85);
151   - message.set("params", params);
152   -
153   - String topic = "/mqtt/productKey/deviceName/property/report";
154   - byte[] payload = message.toString().getBytes(StandardCharsets.UTF_8);
155   -
156   - // 解析消息
157   - IotMqttMessage result = parser.parse(topic, payload);
158   -
159   - // 验证结果
160   - assertNotNull(result);
161   - assertEquals("mqtt001", result.getId());
162   - assertEquals("device.property.report", result.getMethod());
163   - assertEquals(85, ((Number) result.getParams().get("signal")).intValue());
164   - }
165   -
166   - @Test
167   - void testParseDeviceTopicFormat() {
168   - // 测试设备主题格式
169   - JSONObject message = new JSONObject();
170   - message.set("id", "device001");
171   - message.set("version", "1.0");
172   - message.set("method", "sensor.data");
173   -
174   - Map<String, Object> params = new HashMap<>();
175   - params.put("timestamp", System.currentTimeMillis());
176   - message.set("params", params);
177   -
178   - String topic = "/device/productKey/deviceName/sensor/data";
179   - byte[] payload = message.toString().getBytes(StandardCharsets.UTF_8);
180   -
181   - // 解析消息
182   - IotMqttMessage result = parser.parse(topic, payload);
183   -
184   - // 验证结果
185   - assertNotNull(result);
186   - assertEquals("device001", result.getId());
187   - assertEquals("sensor.data", result.getMethod());
188   - assertNotNull(result.getParams().get("timestamp"));
189   - }
190   -}
191 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/util/IotHttpTopicUtilsTest.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.util;
2   -
3   -import org.junit.jupiter.api.Test;
4   -
5   -import static org.junit.jupiter.api.Assertions.*;
6   -
7   -/**
8   - * {@link IotHttpTopicUtils} 单元测试
9   - *
10   - * @author haohao
11   - */
12   -class IotHttpTopicUtilsTest {
13   -
14   - @Test
15   - void testBuildAuthPath() {
16   - assertEquals("/auth", IotHttpTopicUtils.buildAuthPath());
17   - }
18   -
19   - @Test
20   - void testBuildTopicPath() {
21   - // 测试正常路径
22   - assertEquals("/topic/sys/test/device/thing/service/property/set",
23   - IotHttpTopicUtils.buildTopicPath("/sys/test/device/thing/service/property/set"));
24   -
25   - // 测试空路径
26   - assertNull(IotHttpTopicUtils.buildTopicPath(null));
27   - assertNull(IotHttpTopicUtils.buildTopicPath(""));
28   - }
29   -
30   - @Test
31   - void testBuildPropertySetPath() {
32   - String result = IotHttpTopicUtils.buildPropertySetPath("testProduct", "testDevice");
33   - assertEquals("/topic/sys/testProduct/testDevice/thing/service/property/set", result);
34   -
35   - // 测试无效参数
36   - assertNull(IotHttpTopicUtils.buildPropertySetPath(null, "testDevice"));
37   - assertNull(IotHttpTopicUtils.buildPropertySetPath("testProduct", null));
38   - assertNull(IotHttpTopicUtils.buildPropertySetPath("", "testDevice"));
39   - assertNull(IotHttpTopicUtils.buildPropertySetPath("testProduct", ""));
40   - }
41   -
42   - @Test
43   - void testBuildPropertyGetPath() {
44   - String result = IotHttpTopicUtils.buildPropertyGetPath("testProduct", "testDevice");
45   - assertEquals("/topic/sys/testProduct/testDevice/thing/service/property/get", result);
46   - }
47   -
48   - @Test
49   - void testBuildPropertyPostPath() {
50   - String result = IotHttpTopicUtils.buildPropertyPostPath("testProduct", "testDevice");
51   - assertEquals("/topic/sys/testProduct/testDevice/thing/event/property/post", result);
52   - }
53   -
54   - @Test
55   - void testBuildEventPostPath() {
56   - String result = IotHttpTopicUtils.buildEventPostPath("testProduct", "testDevice", "alarm");
57   - assertEquals("/topic/sys/testProduct/testDevice/thing/event/alarm/post", result);
58   -
59   - // 测试无效参数
60   - assertNull(IotHttpTopicUtils.buildEventPostPath(null, "testDevice", "alarm"));
61   - assertNull(IotHttpTopicUtils.buildEventPostPath("testProduct", null, "alarm"));
62   - assertNull(IotHttpTopicUtils.buildEventPostPath("testProduct", "testDevice", null));
63   - }
64   -
65   - @Test
66   - void testBuildServiceInvokePath() {
67   - String result = IotHttpTopicUtils.buildServiceInvokePath("testProduct", "testDevice", "reboot");
68   - assertEquals("/topic/sys/testProduct/testDevice/thing/service/reboot", result);
69   -
70   - // 测试无效参数
71   - assertNull(IotHttpTopicUtils.buildServiceInvokePath(null, "testDevice", "reboot"));
72   - assertNull(IotHttpTopicUtils.buildServiceInvokePath("testProduct", null, "reboot"));
73   - assertNull(IotHttpTopicUtils.buildServiceInvokePath("testProduct", "testDevice", null));
74   - }
75   -
76   - @Test
77   - void testBuildCustomTopicPath() {
78   - String result = IotHttpTopicUtils.buildCustomTopicPath("testProduct", "testDevice", "user/get");
79   - assertEquals("/topic/testProduct/testDevice/user/get", result);
80   -
81   - // 测试无效参数
82   - assertNull(IotHttpTopicUtils.buildCustomTopicPath(null, "testDevice", "user/get"));
83   - assertNull(IotHttpTopicUtils.buildCustomTopicPath("testProduct", null, "user/get"));
84   - assertNull(IotHttpTopicUtils.buildCustomTopicPath("testProduct", "testDevice", null));
85   - }
86   -
87   - @Test
88   - void testExtractActualTopic() {
89   - // 测试正常提取
90   - String actualTopic = IotHttpTopicUtils
91   - .extractActualTopic("/topic/sys/testProduct/testDevice/thing/service/property/set");
92   - assertEquals("/sys/testProduct/testDevice/thing/service/property/set", actualTopic);
93   -
94   - // 测试无效路径
95   - assertNull(IotHttpTopicUtils.extractActualTopic("/auth"));
96   - assertNull(IotHttpTopicUtils.extractActualTopic("/unknown/path"));
97   - assertNull(IotHttpTopicUtils.extractActualTopic(null));
98   - assertNull(IotHttpTopicUtils.extractActualTopic(""));
99   - }
100   -
101   - @Test
102   - void testParseProductKeyFromTopic() {
103   - // 测试系统主题
104   - assertEquals("testProduct",
105   - IotHttpTopicUtils.parseProductKeyFromTopic("/sys/testProduct/testDevice/thing/service/property/set"));
106   -
107   - // 测试自定义主题
108   - assertEquals("testProduct", IotHttpTopicUtils.parseProductKeyFromTopic("/testProduct/testDevice/user/get"));
109   -
110   - // 测试无效主题
111   - assertNull(IotHttpTopicUtils.parseProductKeyFromTopic("/sys"));
112   - assertNull(IotHttpTopicUtils.parseProductKeyFromTopic("/single"));
113   - assertNull(IotHttpTopicUtils.parseProductKeyFromTopic(""));
114   - assertNull(IotHttpTopicUtils.parseProductKeyFromTopic(null));
115   - }
116   -
117   - @Test
118   - void testParseDeviceNameFromTopic() {
119   - // 测试系统主题
120   - assertEquals("testDevice",
121   - IotHttpTopicUtils.parseDeviceNameFromTopic("/sys/testProduct/testDevice/thing/service/property/set"));
122   -
123   - // 测试自定义主题
124   - assertEquals("testDevice", IotHttpTopicUtils.parseDeviceNameFromTopic("/testProduct/testDevice/user/get"));
125   -
126   - // 测试无效主题
127   - assertNull(IotHttpTopicUtils.parseDeviceNameFromTopic("/sys/testProduct"));
128   - assertNull(IotHttpTopicUtils.parseDeviceNameFromTopic("/testProduct"));
129   - assertNull(IotHttpTopicUtils.parseDeviceNameFromTopic(""));
130   - assertNull(IotHttpTopicUtils.parseDeviceNameFromTopic(null));
131   - }
132   -
133   - @Test
134   - void testIsAuthPath() {
135   - assertTrue(IotHttpTopicUtils.isAuthPath("/auth"));
136   - assertFalse(IotHttpTopicUtils.isAuthPath("/topic/test"));
137   - assertFalse(IotHttpTopicUtils.isAuthPath("/unknown"));
138   - assertFalse(IotHttpTopicUtils.isAuthPath(null));
139   - assertFalse(IotHttpTopicUtils.isAuthPath(""));
140   - }
141   -
142   - @Test
143   - void testIsTopicPath() {
144   - assertTrue(IotHttpTopicUtils.isTopicPath("/topic/sys/test/device/property"));
145   - assertTrue(IotHttpTopicUtils.isTopicPath("/topic/test"));
146   - assertFalse(IotHttpTopicUtils.isTopicPath("/auth"));
147   - assertFalse(IotHttpTopicUtils.isTopicPath("/unknown"));
148   - assertFalse(IotHttpTopicUtils.isTopicPath(null));
149   - assertFalse(IotHttpTopicUtils.isTopicPath(""));
150   - }
151   -
152   - @Test
153   - void testIsValidHttpPath() {
154   - assertTrue(IotHttpTopicUtils.isValidHttpPath("/auth"));
155   - assertTrue(IotHttpTopicUtils.isValidHttpPath("/topic/test"));
156   - assertFalse(IotHttpTopicUtils.isValidHttpPath("/unknown"));
157   - assertFalse(IotHttpTopicUtils.isValidHttpPath(null));
158   - assertFalse(IotHttpTopicUtils.isValidHttpPath(""));
159   - }
160   -
161   - @Test
162   - void testIsSystemTopic() {
163   - assertTrue(IotHttpTopicUtils.isSystemTopic("/sys/testProduct/testDevice/thing/service/property/set"));
164   - assertFalse(IotHttpTopicUtils.isSystemTopic("/testProduct/testDevice/user/get"));
165   - assertFalse(IotHttpTopicUtils.isSystemTopic("/unknown"));
166   - assertFalse(IotHttpTopicUtils.isSystemTopic(null));
167   - assertFalse(IotHttpTopicUtils.isSystemTopic(""));
168   - }
169   -
170   - @Test
171   - void testBuildReplyPath() {
172   - // 测试系统主题响应路径
173   - String requestPath = "/topic/sys/testProduct/testDevice/thing/service/property/set";
174   - String replyPath = IotHttpTopicUtils.buildReplyPath(requestPath);
175   - assertEquals("/topic/sys/testProduct/testDevice/thing/service/property/set_reply", replyPath);
176   -
177   - // 测试非系统主题
178   - String customPath = "/topic/testProduct/testDevice/user/get";
179   - assertNull(IotHttpTopicUtils.buildReplyPath(customPath));
180   -
181   - // 测试无效路径
182   - assertNull(IotHttpTopicUtils.buildReplyPath("/auth"));
183   - assertNull(IotHttpTopicUtils.buildReplyPath("/unknown"));
184   - assertNull(IotHttpTopicUtils.buildReplyPath(null));
185   - }
186   -}
187 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-protocol/src/test/java/cn/iocoder/yudao/module/iot/protocol/util/IotTopicUtilsTest.java deleted
1   -package cn.iocoder.yudao.module.iot.protocol.util;
2   -
3   -import org.junit.jupiter.api.Test;
4   -
5   -import static org.junit.jupiter.api.Assertions.assertEquals;
6   -import static org.junit.jupiter.api.Assertions.assertNull;
7   -
8   -/**
9   - * {@link IotTopicUtils} 单元测试
10   - *
11   - * @author haohao
12   - */
13   -class IotTopicUtilsTest {
14   -
15   - @Test
16   - void testBuildPropertySetTopic() {
17   - String topic = IotTopicUtils.buildPropertySetTopic("testProduct", "testDevice");
18   - assertEquals("/sys/testProduct/testDevice/thing/service/property/set", topic);
19   - }
20   -
21   - @Test
22   - void testBuildPropertyGetTopic() {
23   - String topic = IotTopicUtils.buildPropertyGetTopic("testProduct", "testDevice");
24   - assertEquals("/sys/testProduct/testDevice/thing/service/property/get", topic);
25   - }
26   -
27   - @Test
28   - void testBuildEventPostTopic() {
29   - String topic = IotTopicUtils.buildEventPostTopic("testProduct", "testDevice", "temperature");
30   - assertEquals("/sys/testProduct/testDevice/thing/event/temperature/post", topic);
31   - }
32   -
33   - @Test
34   - void testGetReplyTopic() {
35   - String requestTopic = "/sys/testProduct/testDevice/thing/service/property/set";
36   - String replyTopic = IotTopicUtils.getReplyTopic(requestTopic);
37   - assertEquals("/sys/testProduct/testDevice/thing/service/property/set_reply", replyTopic);
38   - }
39   -
40   - @Test
41   - void testParseProductKeyFromTopic() {
42   - String topic = "/sys/testProduct/testDevice/thing/service/property/set";
43   - String productKey = IotTopicUtils.parseProductKeyFromTopic(topic);
44   - assertEquals("testProduct", productKey);
45   - }
46   -
47   - @Test
48   - void testParseDeviceNameFromTopic() {
49   - String topic = "/sys/testProduct/testDevice/thing/service/property/set";
50   - String deviceName = IotTopicUtils.parseDeviceNameFromTopic(topic);
51   - assertEquals("testDevice", deviceName);
52   - }
53   -
54   - @Test
55   - void testParseMethodFromTopic() {
56   - // 测试属性设置
57   - String topic1 = "/sys/testProduct/testDevice/thing/service/property/set";
58   - String method1 = IotTopicUtils.parseMethodFromTopic(topic1);
59   - assertEquals("property.set", method1);
60   -
61   - // 测试事件上报
62   - String topic2 = "/sys/testProduct/testDevice/thing/event/temperature/post";
63   - String method2 = IotTopicUtils.parseMethodFromTopic(topic2);
64   - assertEquals("event.temperature.post", method2);
65   -
66   - // 测试无效主题
67   - String method3 = IotTopicUtils.parseMethodFromTopic("/invalid/topic");
68   - assertNull(method3);
69   - }
70   -
71   - @Test
72   - void testParseInvalidTopic() {
73   - // 测试空主题
74   - assertNull(IotTopicUtils.parseProductKeyFromTopic(""));
75   - assertNull(IotTopicUtils.parseProductKeyFromTopic(null));
76   -
77   - // 测试格式错误的主题
78   - assertNull(IotTopicUtils.parseProductKeyFromTopic("/invalid"));
79   - assertNull(IotTopicUtils.parseDeviceNameFromTopic("/sys/product"));
80   - }
81   -}
82 0 \ No newline at end of file