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,8 +9,6 @@
9 <modules> 9 <modules>
10 <module>yudao-module-iot-api</module> 10 <module>yudao-module-iot-api</module>
11 <module>yudao-module-iot-biz</module> 11 <module>yudao-module-iot-biz</module>
12 - <module>yudao-module-iot-net-components</module>  
13 - <module>yudao-module-iot-protocol</module>  
14 <module>yudao-module-iot-core</module> 12 <module>yudao-module-iot-core</module>
15 <module>yudao-module-iot-gateway</module> 13 <module>yudao-module-iot-gateway</module>
16 </modules> 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,8 +15,5 @@ public class DictTypeConstants {
15 public static final String VALIDATE_TYPE = "iot_validate_type"; 15 public static final String VALIDATE_TYPE = "iot_validate_type";
16 16
17 public static final String DEVICE_STATE = "iot_device_state"; 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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,7 +55,7 @@ public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamServic
55 if (StrUtil.isEmpty(serverId)) { 55 if (StrUtil.isEmpty(serverId)) {
56 throw exception(DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL); 56 throw exception(DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL);
57 } 57 }
58 - deviceMessageProducer.sendGatewayDeviceMessage(serverId, message); 58 + deviceMessageProducer.sendDeviceMessageToGateway(serverId, message);
59 59
60 // 3.2 发送给服务器(用于设备日志等的记录) 60 // 3.2 发送给服务器(用于设备日志等的记录)
61 deviceMessageProducer.sendDeviceMessage(message); 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 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 \ No newline at end of file 0 \ No newline at end of file