Commit a77e1780ccfa9c42d325fe5da7d8e8a1fde7dfcf

Authored by YunaiV
1 parent cc0d786d

feat(iot):【协议改造】移除 codecType,使用 protocolType 替代

Showing 25 changed files with 159 additions and 662 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java
@@ -58,7 +58,7 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi { @@ -58,7 +58,7 @@ public class IoTDeviceApiImpl implements IotDeviceCommonApi {
58 return success(BeanUtils.toBean(device, IotDeviceRespDTO.class, deviceDTO -> { 58 return success(BeanUtils.toBean(device, IotDeviceRespDTO.class, deviceDTO -> {
59 IotProductDO product = productService.getProductFromCache(deviceDTO.getProductId()); 59 IotProductDO product = productService.getProductFromCache(deviceDTO.getProductId());
60 if (product != null) { 60 if (product != null) {
61 - deviceDTO.setCodecType(product.getCodecType()); 61 + deviceDTO.setProtocolType(product.getProtocolType()).setSerializeType(product.getSerializeType());
62 } 62 }
63 })); 63 }));
64 } 64 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java
@@ -67,10 +67,15 @@ public class IotProductRespVO { @@ -67,10 +67,15 @@ public class IotProductRespVO {
67 @DictFormat(DictTypeConstants.NET_TYPE) 67 @DictFormat(DictTypeConstants.NET_TYPE)
68 private Integer netType; 68 private Integer netType;
69 69
70 - @Schema(description = "数据格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")  
71 - @ExcelProperty(value = "数据格式", converter = DictConvert.class)  
72 - @DictFormat(DictTypeConstants.CODEC_TYPE)  
73 - private String codecType; 70 + @Schema(description = "协议类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "mqtt")
  71 + @ExcelProperty(value = "协议类型", converter = DictConvert.class)
  72 + @DictFormat(DictTypeConstants.PROTOCOL_TYPE)
  73 + private String protocolType;
  74 +
  75 + @Schema(description = "序列化类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "json")
  76 + @ExcelProperty(value = "序列化类型", converter = DictConvert.class)
  77 + @DictFormat(DictTypeConstants.SERIALIZE_TYPE)
  78 + private String serializeType;
74 79
75 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) 80 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
76 @ExcelProperty("创建时间") 81 @ExcelProperty("创建时间")
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java
1 package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product; 1 package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product;
2 2
3 import cn.iocoder.yudao.framework.common.validation.InEnum; 3 import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum;
  5 +import cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;
4 import cn.iocoder.yudao.module.iot.enums.product.IotNetTypeEnum; 6 import cn.iocoder.yudao.module.iot.enums.product.IotNetTypeEnum;
5 import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; 7 import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum;
6 import io.swagger.v3.oas.annotations.media.Schema; 8 import io.swagger.v3.oas.annotations.media.Schema;
@@ -44,9 +46,15 @@ public class IotProductSaveReqVO { @@ -44,9 +46,15 @@ public class IotProductSaveReqVO {
44 @InEnum(value = IotNetTypeEnum.class, message = "联网方式必须是 {value}") 46 @InEnum(value = IotNetTypeEnum.class, message = "联网方式必须是 {value}")
45 private Integer netType; 47 private Integer netType;
46 48
47 - @Schema(description = "数据格式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")  
48 - @NotEmpty(message = "数据格式不能为空")  
49 - private String codecType; 49 + @Schema(description = "协议类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "mqtt")
  50 + @InEnum(value = IotProtocolTypeEnum.class, message = "协议类型必须是 {value}")
  51 + @NotEmpty(message = "协议类型不能为空")
  52 + private String protocolType;
  53 +
  54 + @Schema(description = "序列化类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "json")
  55 + @InEnum(value = IotSerializeTypeEnum.class, message = "序列化类型必须是 {value}")
  56 + @NotEmpty(message = "序列化类型不能为空")
  57 + private String serializeType;
50 58
51 @Schema(description = "是否开启动态注册", example = "false") 59 @Schema(description = "是否开启动态注册", example = "false")
52 @NotNull(message = "是否开启动态注册不能为空") 60 @NotNull(message = "是否开启动态注册不能为空")
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/product/IotProductDO.java
@@ -78,12 +78,16 @@ public class IotProductDO extends TenantBaseDO { @@ -78,12 +78,16 @@ public class IotProductDO extends TenantBaseDO {
78 */ 78 */
79 private Integer netType; 79 private Integer netType;
80 /** 80 /**
81 - * 数据格式(编解码器类型) 81 + * 协议类型
82 * <p> 82 * <p>
83 - * 字典 {@link cn.iocoder.yudao.module.iot.enums.DictTypeConstants#CODEC_TYPE}  
84 - *  
85 - * 目的:用于 gateway-server 解析消息格式 83 + * 枚举 {@link cn.iocoder.yudao.module.iot.core.enums.IotProtocolTypeEnum}
86 */ 84 */
87 - private String codecType; 85 + private String protocolType;
  86 + /**
  87 + * 序列化类型
  88 + * <p>
  89 + * 枚举 {@link cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum}
  90 + */
  91 + private String serializeType;
88 92
89 } 93 }
90 \ No newline at end of file 94 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/DictTypeConstants.java
@@ -8,8 +8,8 @@ package cn.iocoder.yudao.module.iot.enums; @@ -8,8 +8,8 @@ package cn.iocoder.yudao.module.iot.enums;
8 public class DictTypeConstants { 8 public class DictTypeConstants {
9 9
10 public static final String NET_TYPE = "iot_net_type"; 10 public static final String NET_TYPE = "iot_net_type";
11 - public static final String LOCATION_TYPE = "iot_location_type";  
12 - public static final String CODEC_TYPE = "iot_codec_type"; 11 + public static final String PROTOCOL_TYPE = "iot_protocol_type";
  12 + public static final String SERIALIZE_TYPE = "iot_serialize_type";
13 13
14 public static final String PRODUCT_STATUS = "iot_product_status"; 14 public static final String PRODUCT_STATUS = "iot_product_status";
15 public static final String PRODUCT_DEVICE_TYPE = "iot_product_device_type"; 15 public static final String PRODUCT_DEVICE_TYPE = "iot_product_device_type";
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/biz/dto/IotDeviceRespDTO.java
@@ -34,8 +34,12 @@ public class IotDeviceRespDTO { @@ -34,8 +34,12 @@ public class IotDeviceRespDTO {
34 */ 34 */
35 private Long productId; 35 private Long productId;
36 /** 36 /**
37 - * 编解码器类型 37 + * 协议类型
38 */ 38 */
39 - private String codecType; 39 + private String protocolType;
  40 + /**
  41 + * 序列化类型
  42 + */
  43 + private String serializeType;
40 44
41 } 45 }
42 \ No newline at end of file 46 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/IotDeviceMessageCodec.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.codec;  
2 -  
3 -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;  
4 -  
5 -/**  
6 - * {@link cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage} 的编解码器  
7 - *  
8 - * @author 芋道源码  
9 - */  
10 -public interface IotDeviceMessageCodec {  
11 -  
12 - /**  
13 - * 编码消息  
14 - *  
15 - * @param message 消息  
16 - * @return 编码后的消息内容  
17 - */  
18 - byte[] encode(IotDeviceMessage message);  
19 -  
20 - /**  
21 - * 解码消息  
22 - *  
23 - * @param bytes 消息内容  
24 - * @return 解码后的消息内容  
25 - */  
26 - IotDeviceMessage decode(byte[] bytes);  
27 -  
28 - /**  
29 - * @return 数据格式(编码器类型)  
30 - */  
31 - String type();  
32 -  
33 -}  
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/alink/IotAlinkDeviceMessageCodec.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.codec.alink;  
2 -  
3 -import cn.hutool.core.lang.Assert;  
4 -import cn.iocoder.yudao.framework.common.pojo.CommonResult;  
5 -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;  
6 -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;  
7 -import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec;  
8 -import lombok.AllArgsConstructor;  
9 -import lombok.Data;  
10 -import lombok.NoArgsConstructor;  
11 -import org.springframework.stereotype.Component;  
12 -  
13 -/**  
14 - * 阿里云 Alink {@link IotDeviceMessage} 的编解码器  
15 - *  
16 - * @author 芋道源码  
17 - */  
18 -@Component  
19 -public class IotAlinkDeviceMessageCodec implements IotDeviceMessageCodec {  
20 -  
21 - public static final String TYPE = "Alink";  
22 -  
23 - @Data  
24 - @NoArgsConstructor  
25 - @AllArgsConstructor  
26 - private static class AlinkMessage {  
27 -  
28 - public static final String VERSION_1 = "1.0";  
29 -  
30 - /**  
31 - * 消息 ID,且每个消息 ID 在当前设备具有唯一性  
32 - */  
33 - private String id;  
34 -  
35 - /**  
36 - * 版本号  
37 - */  
38 - private String version;  
39 -  
40 - /**  
41 - * 请求方法  
42 - */  
43 - private String method;  
44 -  
45 - /**  
46 - * 请求参数  
47 - */  
48 - private Object params;  
49 -  
50 - /**  
51 - * 响应结果  
52 - */  
53 - private Object data;  
54 - /**  
55 - * 响应错误码  
56 - */  
57 - private Integer code;  
58 - /**  
59 - * 响应提示  
60 - *  
61 - * 特殊:这里阿里云是 message,为了保持和项目的 {@link CommonResult#getMsg()} 一致。  
62 - */  
63 - private String msg;  
64 -  
65 - }  
66 -  
67 - @Override  
68 - public String type() {  
69 - return TYPE;  
70 - }  
71 -  
72 - @Override  
73 - public byte[] encode(IotDeviceMessage message) {  
74 - AlinkMessage alinkMessage = new AlinkMessage(message.getRequestId(), AlinkMessage.VERSION_1,  
75 - message.getMethod(), message.getParams(), message.getData(), message.getCode(), message.getMsg());  
76 - return JsonUtils.toJsonByte(alinkMessage);  
77 - }  
78 -  
79 - @Override  
80 - @SuppressWarnings("DataFlowIssue")  
81 - public IotDeviceMessage decode(byte[] bytes) {  
82 - AlinkMessage alinkMessage = JsonUtils.parseObject(bytes, AlinkMessage.class);  
83 - Assert.notNull(alinkMessage, "消息不能为空");  
84 - Assert.equals(alinkMessage.getVersion(), AlinkMessage.VERSION_1, "消息版本号必须是 1.0");  
85 - return IotDeviceMessage.of(alinkMessage.getId(), alinkMessage.getMethod(), alinkMessage.getParams(),  
86 - alinkMessage.getData(), alinkMessage.getCode(), alinkMessage.getMsg());  
87 - }  
88 -  
89 -}  
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/package-info.java deleted
1 -/**  
2 - * 提供设备接入的各种数据(请求、响应)的编解码  
3 - */  
4 -package cn.iocoder.yudao.module.iot.gateway.codec;  
5 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/tcp/IotTcpBinaryDeviceMessageCodec.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.codec.tcp;  
2 -  
3 -import cn.hutool.core.lang.Assert;  
4 -import cn.hutool.core.util.StrUtil;  
5 -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;  
6 -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;  
7 -import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;  
8 -import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec;  
9 -import io.vertx.core.buffer.Buffer;  
10 -import lombok.extern.slf4j.Slf4j;  
11 -import org.springframework.stereotype.Component;  
12 -  
13 -import java.nio.charset.StandardCharsets;  
14 -  
15 -/**  
16 - * TCP/UDP 二进制格式 {@link IotDeviceMessage} 编解码器  
17 - * <p>  
18 - * 二进制协议格式(所有数值使用大端序):  
19 - *  
20 - * <pre>  
21 - * +--------+--------+--------+---------------------------+--------+--------+  
22 - * | 魔术字 | 版本号 | 消息类型| 消息长度(4 字节) |  
23 - * +--------+--------+--------+---------------------------+--------+--------+  
24 - * | 消息 ID 长度(2 字节) | 消息 ID (变长字符串) |  
25 - * +--------+--------+--------+--------+--------+--------+--------+--------+  
26 - * | 方法名长度(2 字节) | 方法名(变长字符串) |  
27 - * +--------+--------+--------+--------+--------+--------+--------+--------+  
28 - * | 消息体数据(变长) |  
29 - * +--------+--------+--------+--------+--------+--------+--------+--------+  
30 - * </pre>  
31 - * <p>  
32 - * 消息体格式:  
33 - * - 请求消息:params 数据(JSON)  
34 - * - 响应消息:code (4字节) + msg 长度(2字节) + msg 字符串 + data 数据(JSON)  
35 - * <p>  
36 - * 注意:deviceId 不包含在协议中,由服务器根据连接上下文自动设置  
37 - *  
38 - * @author 芋道源码  
39 - */  
40 -@Slf4j  
41 -@Component  
42 -public class IotTcpBinaryDeviceMessageCodec implements IotDeviceMessageCodec {  
43 -  
44 - public static final String TYPE = "TCP_BINARY";  
45 -  
46 - /**  
47 - * 协议魔术字,用于协议识别  
48 - */  
49 - private static final byte MAGIC_NUMBER = (byte) 0x7E;  
50 -  
51 - /**  
52 - * 协议版本号  
53 - */  
54 - private static final byte PROTOCOL_VERSION = (byte) 0x01;  
55 -  
56 - /**  
57 - * 请求消息类型  
58 - */  
59 - private static final byte REQUEST = (byte) 0x01;  
60 -  
61 - /**  
62 - * 响应消息类型  
63 - */  
64 - private static final byte RESPONSE = (byte) 0x02;  
65 -  
66 - /**  
67 - * 协议头部固定长度(魔术字 + 版本号 + 消息类型 + 消息长度)  
68 - */  
69 - private static final int HEADER_FIXED_LENGTH = 7;  
70 -  
71 - /**  
72 - * 最小消息长度(头部 + 消息ID长度 + 方法名长度)  
73 - */  
74 - private static final int MIN_MESSAGE_LENGTH = HEADER_FIXED_LENGTH + 4;  
75 -  
76 - @Override  
77 - public String type() {  
78 - return TYPE;  
79 - }  
80 -  
81 - @Override  
82 - public byte[] encode(IotDeviceMessage message) {  
83 - Assert.notNull(message, "消息不能为空");  
84 - Assert.notBlank(message.getMethod(), "消息方法不能为空");  
85 - try {  
86 - // 1. 确定消息类型  
87 - byte messageType = determineMessageType(message);  
88 - // 2. 构建消息体  
89 - byte[] bodyData = buildMessageBody(message, messageType);  
90 - // 3. 构建完整消息  
91 - return buildCompleteMessage(message, messageType, bodyData);  
92 - } catch (Exception e) {  
93 - log.error("[encode][TCP 二进制消息编码失败,消息: {}]", message, e);  
94 - throw new RuntimeException("TCP 二进制消息编码失败: " + e.getMessage(), e);  
95 - }  
96 - }  
97 -  
98 - @Override  
99 - public IotDeviceMessage decode(byte[] bytes) {  
100 - Assert.notNull(bytes, "待解码数据不能为空");  
101 - Assert.isTrue(bytes.length >= MIN_MESSAGE_LENGTH, "数据包长度不足");  
102 - try {  
103 - Buffer buffer = Buffer.buffer(bytes);  
104 - // 解析协议头部和消息内容  
105 - int index = 0;  
106 - // 1. 验证魔术字  
107 - byte magic = buffer.getByte(index++);  
108 - Assert.isTrue(magic == MAGIC_NUMBER, "无效的协议魔术字: " + magic);  
109 -  
110 - // 2. 验证版本号  
111 - byte version = buffer.getByte(index++);  
112 - Assert.isTrue(version == PROTOCOL_VERSION, "不支持的协议版本: " + version);  
113 -  
114 - // 3. 读取消息类型  
115 - byte messageType = buffer.getByte(index++);  
116 - // 直接验证消息类型,无需抽取方法  
117 - Assert.isTrue(messageType == REQUEST || messageType == RESPONSE,  
118 - "无效的消息类型: " + messageType);  
119 -  
120 - // 4. 读取消息长度  
121 - int messageLength = buffer.getInt(index);  
122 - index += 4;  
123 - Assert.isTrue(messageLength == buffer.length(),  
124 - "消息长度不匹配,期望: " + messageLength + ", 实际: " + buffer.length());  
125 -  
126 - // 5. 读取消息 ID  
127 - short messageIdLength = buffer.getShort(index);  
128 - index += 2;  
129 - String messageId = buffer.getString(index, index + messageIdLength, StandardCharsets.UTF_8.name());  
130 - index += messageIdLength;  
131 -  
132 - // 6. 读取方法名  
133 - short methodLength = buffer.getShort(index);  
134 - index += 2;  
135 - String method = buffer.getString(index, index + methodLength, StandardCharsets.UTF_8.name());  
136 - index += methodLength;  
137 -  
138 - // 7. 解析消息体  
139 - return parseMessageBody(buffer, index, messageType, messageId, method);  
140 - } catch (Exception e) {  
141 - log.error("[decode][TCP 二进制消息解码失败,数据长度: {}]", bytes.length, e);  
142 - throw new RuntimeException("TCP 二进制消息解码失败: " + e.getMessage(), e);  
143 - }  
144 - }  
145 -  
146 - /**  
147 - * 确定消息类型  
148 - * 优化后的判断逻辑:有响应字段就是响应消息,否则就是请求消息  
149 - */  
150 - private byte determineMessageType(IotDeviceMessage message) {  
151 - // 判断是否为响应消息:有响应码或响应消息时为响应  
152 - if (message.getCode() != null) {  
153 - return RESPONSE;  
154 - }  
155 - // 默认为请求消息  
156 - return REQUEST;  
157 - }  
158 -  
159 - /**  
160 - * 构建消息体  
161 - */  
162 - private byte[] buildMessageBody(IotDeviceMessage message, byte messageType) {  
163 - Buffer bodyBuffer = Buffer.buffer();  
164 - if (messageType == RESPONSE) {  
165 - // code  
166 - bodyBuffer.appendInt(message.getCode() != null ? message.getCode() : 0);  
167 - // msg  
168 - String msg = message.getMsg() != null ? message.getMsg() : "";  
169 - byte[] msgBytes = StrUtil.utf8Bytes(msg);  
170 - bodyBuffer.appendShort((short) msgBytes.length);  
171 - bodyBuffer.appendBytes(msgBytes);  
172 - // data  
173 - if (message.getData() != null) {  
174 - bodyBuffer.appendBytes(JsonUtils.toJsonByte(message.getData()));  
175 - }  
176 - } else {  
177 - // 请求消息只处理 params 参数  
178 - // TODO @haohao:如果为空,是不是得写个长度 0 哈?  
179 - if (message.getParams() != null) {  
180 - bodyBuffer.appendBytes(JsonUtils.toJsonByte(message.getParams()));  
181 - }  
182 - }  
183 - return bodyBuffer.getBytes();  
184 - }  
185 -  
186 - /**  
187 - * 构建完整消息  
188 - */  
189 - private byte[] buildCompleteMessage(IotDeviceMessage message, byte messageType, byte[] bodyData) {  
190 - Buffer buffer = Buffer.buffer();  
191 - // 1. 写入协议头部  
192 - buffer.appendByte(MAGIC_NUMBER);  
193 - buffer.appendByte(PROTOCOL_VERSION);  
194 - buffer.appendByte(messageType);  
195 - // 2. 预留消息长度位置(在 5. 更新消息长度)  
196 - int lengthPosition = buffer.length();  
197 - buffer.appendInt(0);  
198 - // 3. 写入消息 ID  
199 - String messageId = StrUtil.isNotBlank(message.getRequestId()) ? message.getRequestId()  
200 - : IotDeviceMessageUtils.generateMessageId();  
201 - byte[] messageIdBytes = StrUtil.utf8Bytes(messageId);  
202 - buffer.appendShort((short) messageIdBytes.length);  
203 - buffer.appendBytes(messageIdBytes);  
204 - // 4. 写入方法名  
205 - byte[] methodBytes = StrUtil.utf8Bytes(message.getMethod());  
206 - buffer.appendShort((short) methodBytes.length);  
207 - buffer.appendBytes(methodBytes);  
208 - // 5. 写入消息体  
209 - buffer.appendBytes(bodyData);  
210 - // 6. 更新消息长度  
211 - buffer.setInt(lengthPosition, buffer.length());  
212 - return buffer.getBytes();  
213 - }  
214 -  
215 - /**  
216 - * 解析消息体  
217 - */  
218 - private IotDeviceMessage parseMessageBody(Buffer buffer, int startIndex, byte messageType,  
219 - String messageId, String method) {  
220 - if (startIndex >= buffer.length()) {  
221 - // 空消息体  
222 - return IotDeviceMessage.of(messageId, method, null, null, null, null);  
223 - }  
224 -  
225 - if (messageType == RESPONSE) {  
226 - // 响应消息:解析 code + msg + data  
227 - return parseResponseMessage(buffer, startIndex, messageId, method);  
228 - } else {  
229 - // 请求消息:解析 payload  
230 - Object payload = parseJsonData(buffer, startIndex, buffer.length());  
231 - return IotDeviceMessage.of(messageId, method, payload, null, null, null);  
232 - }  
233 - }  
234 -  
235 - /**  
236 - * 解析响应消息  
237 - */  
238 - private IotDeviceMessage parseResponseMessage(Buffer buffer, int startIndex, String messageId, String method) {  
239 - int index = startIndex;  
240 -  
241 - // 1. 读取响应码  
242 - Integer code = buffer.getInt(index);  
243 - index += 4;  
244 -  
245 - // 2. 读取响应消息  
246 - short msgLength = buffer.getShort(index);  
247 - index += 2;  
248 - String msg = msgLength > 0 ? buffer.getString(index, index + msgLength, StandardCharsets.UTF_8.name()) : null;  
249 - index += msgLength;  
250 -  
251 - // 3. 读取响应数据  
252 - Object data = null;  
253 - if (index < buffer.length()) {  
254 - data = parseJsonData(buffer, index, buffer.length());  
255 - }  
256 -  
257 - return IotDeviceMessage.of(messageId, method, null, data, code, msg);  
258 - }  
259 -  
260 - /**  
261 - * 解析 JSON 数据  
262 - */  
263 - private Object parseJsonData(Buffer buffer, int startIndex, int endIndex) {  
264 - if (startIndex >= endIndex) {  
265 - return null;  
266 - }  
267 - try {  
268 - String jsonStr = buffer.getString(startIndex, endIndex, StandardCharsets.UTF_8.name());  
269 - return JsonUtils.parseObject(jsonStr, Object.class);  
270 - } catch (Exception e) {  
271 - log.warn("[parseJsonData][JSON 解析失败,返回原始字符串]", e);  
272 - return buffer.getString(startIndex, endIndex, StandardCharsets.UTF_8.name());  
273 - }  
274 - }  
275 -  
276 - /**  
277 - * 快速检测是否为二进制格式  
278 - *  
279 - * @param data 数据  
280 - * @return 是否为二进制格式  
281 - */  
282 - public static boolean isBinaryFormatQuick(byte[] data) {  
283 - return data != null && data.length >= 1 && data[0] == MAGIC_NUMBER;  
284 - }  
285 -  
286 -}  
287 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/tcp/IotTcpJsonDeviceMessageCodec.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.codec.tcp;  
2 -  
3 -import cn.hutool.core.lang.Assert;  
4 -import cn.hutool.core.util.StrUtil;  
5 -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;  
6 -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;  
7 -import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec;  
8 -import lombok.AllArgsConstructor;  
9 -import lombok.Data;  
10 -import lombok.NoArgsConstructor;  
11 -import org.springframework.stereotype.Component;  
12 -  
13 -/**  
14 - * TCP/UDP JSON 格式 {@link IotDeviceMessage} 编解码器  
15 - *  
16 - * 采用纯 JSON 格式传输,格式如下:  
17 - * {  
18 - * "id": "消息 ID",  
19 - * "method": "消息方法",  
20 - * "params": {...}, // 请求参数  
21 - * "data": {...}, // 响应结果  
22 - * "code": 200, // 响应错误码  
23 - * "msg": "success", // 响应提示  
24 - * "timestamp": 时间戳  
25 - * }  
26 - *  
27 - * @author 芋道源码  
28 - */  
29 -@Component  
30 -public class IotTcpJsonDeviceMessageCodec implements IotDeviceMessageCodec {  
31 -  
32 - public static final String TYPE = "TCP_JSON";  
33 -  
34 - @Data  
35 - @NoArgsConstructor  
36 - @AllArgsConstructor  
37 - private static class TcpJsonMessage {  
38 -  
39 - /**  
40 - * 消息 ID,且每个消息 ID 在当前设备具有唯一性  
41 - */  
42 - private String id;  
43 -  
44 - /**  
45 - * 请求方法  
46 - */  
47 - private String method;  
48 -  
49 - /**  
50 - * 请求参数  
51 - */  
52 - private Object params;  
53 -  
54 - /**  
55 - * 响应结果  
56 - */  
57 - private Object data;  
58 -  
59 - /**  
60 - * 响应错误码  
61 - */  
62 - private Integer code;  
63 -  
64 - /**  
65 - * 响应提示  
66 - */  
67 - private String msg;  
68 -  
69 - /**  
70 - * 时间戳  
71 - */  
72 - private Long timestamp;  
73 -  
74 - }  
75 -  
76 - @Override  
77 - public String type() {  
78 - return TYPE;  
79 - }  
80 -  
81 - @Override  
82 - public byte[] encode(IotDeviceMessage message) {  
83 - TcpJsonMessage tcpJsonMessage = new TcpJsonMessage(  
84 - message.getRequestId(),  
85 - message.getMethod(),  
86 - message.getParams(),  
87 - message.getData(),  
88 - message.getCode(),  
89 - message.getMsg(),  
90 - System.currentTimeMillis());  
91 - return JsonUtils.toJsonByte(tcpJsonMessage);  
92 - }  
93 -  
94 - @Override  
95 - @SuppressWarnings("DataFlowIssue")  
96 - public IotDeviceMessage decode(byte[] bytes) {  
97 - String jsonStr = StrUtil.utf8Str(bytes).trim();  
98 - TcpJsonMessage tcpJsonMessage = JsonUtils.parseObject(jsonStr, TcpJsonMessage.class);  
99 - Assert.notNull(tcpJsonMessage, "消息不能为空");  
100 - Assert.notBlank(tcpJsonMessage.getMethod(), "消息方法不能为空");  
101 - return IotDeviceMessage.of(  
102 - tcpJsonMessage.getId(),  
103 - tcpJsonMessage.getMethod(),  
104 - tcpJsonMessage.getParams(),  
105 - tcpJsonMessage.getData(),  
106 - tcpJsonMessage.getCode(),  
107 - tcpJsonMessage.getMsg());  
108 - }  
109 -  
110 -}  
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/handler/downstream/IotEmqxDownstreamHandler.java
@@ -53,9 +53,10 @@ public class IotEmqxDownstreamHandler { @@ -53,9 +53,10 @@ public class IotEmqxDownstreamHandler {
53 return; 53 return;
54 } 54 }
55 // 2.2 构建载荷 55 // 2.2 构建载荷
56 - byte[] payload = deviceMessageService.encodeDeviceMessage(message, deviceInfo.getProductKey(), 56 + byte[] payload = deviceMessageService.serializeDeviceMessage(message, deviceInfo.getProductKey(),
57 deviceInfo.getDeviceName()); 57 deviceInfo.getDeviceName());
58 - // 2.3 发布消息 58 +
  59 + // 3. 发布消息
59 protocol.publishMessage(topic, payload); 60 protocol.publishMessage(topic, payload);
60 } 61 }
61 62
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/handler/upstream/IotEmqxAuthEventHandler.java
@@ -452,16 +452,17 @@ public class IotEmqxAuthEventHandler { @@ -452,16 +452,17 @@ public class IotEmqxAuthEventHandler {
452 IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(username); 452 IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(username);
453 Assert.notNull(deviceInfo, "设备信息不能为空"); 453 Assert.notNull(deviceInfo, "设备信息不能为空");
454 try { 454 try {
455 - // 1. 构建响应消息 455 + // 1.1 构建响应消息
456 String method = IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod(); 456 String method = IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod();
457 IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(null, method, result, 0, null); 457 IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(null, method, result, 0, null);
458 -  
459 - // 2. 编码消息  
460 - byte[] encodedData = deviceMessageService.encodeDeviceMessage(responseMessage, "Alink");  
461 -  
462 - // 3. 构建响应主题并延迟发布(等待设备连接成功并完成订阅) 458 + // 1.2 序列化消息
  459 + byte[] encodedData = deviceMessageService.serializeDeviceMessage(responseMessage,
  460 + cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum.JSON);
  461 + // 1.3 构建响应主题
463 String replyTopic = IotMqttTopicUtils.buildTopicByMethod(method, 462 String replyTopic = IotMqttTopicUtils.buildTopicByMethod(method,
464 deviceInfo.getProductKey(), deviceInfo.getDeviceName(), true); 463 deviceInfo.getProductKey(), deviceInfo.getDeviceName(), true);
  464 +
  465 + // 2. 构建响应主题,并延迟发布(等待设备连接成功并完成订阅)
465 protocol.publishDelayMessage(replyTopic, encodedData, 5000); 466 protocol.publishDelayMessage(replyTopic, encodedData, 5000);
466 log.info("[sendRegisterResultMessage][发送注册结果: topic={}]", replyTopic); 467 log.info("[sendRegisterResultMessage][发送注册结果: topic={}]", replyTopic);
467 } catch (Exception e) { 468 } catch (Exception e) {
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/handler/upstream/IotEmqxUpstreamHandler.java
1 package cn.iocoder.yudao.module.iot.gateway.protocol.emqx.handler.upstream; 1 package cn.iocoder.yudao.module.iot.gateway.protocol.emqx.handler.upstream;
2 2
  3 +import cn.hutool.core.util.ArrayUtil;
3 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
4 import cn.hutool.extra.spring.SpringUtil; 5 import cn.hutool.extra.spring.SpringUtil;
5 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
@@ -34,22 +35,21 @@ public class IotEmqxUpstreamHandler { @@ -34,22 +35,21 @@ public class IotEmqxUpstreamHandler {
34 try { 35 try {
35 // 1. 解析主题,一次性获取所有信息 36 // 1. 解析主题,一次性获取所有信息
36 String[] topicParts = topic.split("/"); 37 String[] topicParts = topic.split("/");
37 - if (topicParts.length < 4 || StrUtil.hasBlank(topicParts[2], topicParts[3])) { 38 + String productKey = ArrayUtil.get(topicParts, 2);
  39 + String deviceName = ArrayUtil.get(topicParts, 3);
  40 + if (topicParts.length < 4 || StrUtil.hasBlank(productKey, deviceName)) {
38 log.warn("[handle][topic({}) 格式不正确,无法解析有效的 productKey 和 deviceName]", topic); 41 log.warn("[handle][topic({}) 格式不正确,无法解析有效的 productKey 和 deviceName]", topic);
39 return; 42 return;
40 } 43 }
41 44
42 - String productKey = topicParts[2];  
43 - String deviceName = topicParts[3];  
44 -  
45 - // 3. 解码消息  
46 - IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(payload, productKey, deviceName); 45 + // 2. 反序列化消息
  46 + IotDeviceMessage message = deviceMessageService.deserializeDeviceMessage(payload, productKey, deviceName);
47 if (message == null) { 47 if (message == null) {
48 log.warn("[handle][topic({}) payload({}) 消息解码失败]", topic, new String(payload)); 48 log.warn("[handle][topic({}) payload({}) 消息解码失败]", topic, new String(payload));
49 return; 49 return;
50 } 50 }
51 51
52 - // 4. 发送消息到队列 52 + // 3. 发送消息到队列
53 deviceMessageService.sendDeviceMessage(message, productKey, deviceName, serverId); 53 deviceMessageService.sendDeviceMessage(message, productKey, deviceName, serverId);
54 } catch (Exception e) { 54 } catch (Exception e) {
55 log.error("[handle][topic({}) payload({}) 处理异常]", topic, new String(payload), e); 55 log.error("[handle][topic({}) payload({}) 处理异常]", topic, new String(payload), e);
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/handler/downstream/IotMqttDownstreamHandler.java
@@ -43,7 +43,7 @@ public class IotMqttDownstreamHandler { @@ -43,7 +43,7 @@ public class IotMqttDownstreamHandler {
43 } 43 }
44 44
45 // 2.1 序列化消息 45 // 2.1 序列化消息
46 - byte[] payload = deviceMessageService.encodeDeviceMessage(message, connectionInfo.getProductKey(), 46 + byte[] payload = deviceMessageService.serializeDeviceMessage(message, connectionInfo.getProductKey(),
47 connectionInfo.getDeviceName()); 47 connectionInfo.getDeviceName());
48 Assert.isTrue(payload != null && payload.length > 0, "消息编码结果不能为空"); 48 Assert.isTrue(payload != null && payload.length > 0, "消息编码结果不能为空");
49 // 2.2 构建主题 49 // 2.2 构建主题
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/handler/upstream/IotMqttAbstractHandler.java
@@ -21,12 +21,6 @@ import lombok.extern.slf4j.Slf4j; @@ -21,12 +21,6 @@ import lombok.extern.slf4j.Slf4j;
21 @RequiredArgsConstructor 21 @RequiredArgsConstructor
22 public abstract class IotMqttAbstractHandler { 22 public abstract class IotMqttAbstractHandler {
23 23
24 - // TODO @AI:当前使用 Alink 序列化类型,后续可考虑支持更多序列化方式  
25 - /**  
26 - * 默认编解码类型(MQTT 使用 Alink 协议)  
27 - */  
28 - protected static final String DEFAULT_CODEC_TYPE = "Alink";  
29 -  
30 protected final IotMqttConnectionManager connectionManager; 24 protected final IotMqttConnectionManager connectionManager;
31 protected final IotDeviceMessageService deviceMessageService; 25 protected final IotDeviceMessageService deviceMessageService;
32 26
@@ -40,8 +34,9 @@ public abstract class IotMqttAbstractHandler { @@ -40,8 +34,9 @@ public abstract class IotMqttAbstractHandler {
40 * @param method 方法名 34 * @param method 方法名
41 * @param data 响应数据 35 * @param data 响应数据
42 */ 36 */
  37 + @SuppressWarnings("SameParameterValue")
43 protected void sendSuccessResponse(MqttEndpoint endpoint, String productKey, String deviceName, 38 protected void sendSuccessResponse(MqttEndpoint endpoint, String productKey, String deviceName,
44 - String requestId, String method, Object data) { 39 + String requestId, String method, Object data) {
45 IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(requestId, method, data, 0, null); 40 IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(requestId, method, data, 0, null);
46 writeResponse(endpoint, productKey, deviceName, method, responseMessage); 41 writeResponse(endpoint, productKey, deviceName, method, responseMessage);
47 } 42 }
@@ -75,11 +70,12 @@ public abstract class IotMqttAbstractHandler { @@ -75,11 +70,12 @@ public abstract class IotMqttAbstractHandler {
75 private void writeResponse(MqttEndpoint endpoint, String productKey, String deviceName, 70 private void writeResponse(MqttEndpoint endpoint, String productKey, String deviceName,
76 String method, IotDeviceMessage responseMessage) { 71 String method, IotDeviceMessage responseMessage) {
77 try { 72 try {
78 - // 1. 编码消息(使用默认编解码器)  
79 - byte[] encodedData = deviceMessageService.encodeDeviceMessage(responseMessage, DEFAULT_CODEC_TYPE);  
80 -  
81 - // 2. 构建响应主题,并发送 73 + // 1.1 序列化消息(根据设备配置的序列化类型)
  74 + byte[] encodedData = deviceMessageService.serializeDeviceMessage(responseMessage, productKey, deviceName);
  75 + // 1.2 构建响应主题
82 String replyTopic = IotMqttTopicUtils.buildTopicByMethod(method, productKey, deviceName, true); 76 String replyTopic = IotMqttTopicUtils.buildTopicByMethod(method, productKey, deviceName, true);
  77 +
  78 + // 2. 发送响应消息
83 endpoint.publish(replyTopic, Buffer.buffer(encodedData), MqttQoS.AT_LEAST_ONCE, false, false); 79 endpoint.publish(replyTopic, Buffer.buffer(encodedData), MqttQoS.AT_LEAST_ONCE, false, false);
84 log.debug("[writeResponse][发送响应,主题: {},code: {}]", replyTopic, responseMessage.getCode()); 80 log.debug("[writeResponse][发送响应,主题: {},code: {}]", replyTopic, responseMessage.getCode());
85 } catch (Exception e) { 81 } catch (Exception e) {
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/handler/upstream/IotMqttRegisterHandler.java
@@ -14,6 +14,8 @@ import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessa @@ -14,6 +14,8 @@ import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessa
14 import io.vertx.mqtt.MqttEndpoint; 14 import io.vertx.mqtt.MqttEndpoint;
15 import lombok.extern.slf4j.Slf4j; 15 import lombok.extern.slf4j.Slf4j;
16 16
  17 +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR;
  18 +
17 /** 19 /**
18 * IoT 网关 MQTT 设备注册处理器:处理设备动态注册消息(一型一密) 20 * IoT 网关 MQTT 设备注册处理器:处理设备动态注册消息(一型一密)
19 * 21 *
@@ -76,7 +78,8 @@ public class IotMqttRegisterHandler extends IotMqttAbstractHandler { @@ -76,7 +78,8 @@ public class IotMqttRegisterHandler extends IotMqttAbstractHandler {
76 log.warn("[handleRegister][注册失败,客户端 ID: {},错误: {}]", clientId, e.getMessage()); 78 log.warn("[handleRegister][注册失败,客户端 ID: {},错误: {}]", clientId, e.getMessage());
77 // 接受连接,并发送错误响应 79 // 接受连接,并发送错误响应
78 endpoint.accept(false); 80 endpoint.accept(false);
79 - sendErrorResponse(endpoint, productKey, deviceName, null, method, 500, e.getMessage()); 81 + sendErrorResponse(endpoint, productKey, deviceName, null, method,
  82 + INTERNAL_SERVER_ERROR.getCode(), e.getMessage());
80 } finally { 83 } finally {
81 // 注册完成后关闭连接(一型一密只用于获取 deviceSecret,不保持连接) 84 // 注册完成后关闭连接(一型一密只用于获取 deviceSecret,不保持连接)
82 endpoint.close(); 85 endpoint.close();
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/handler/upstream/IotMqttUpstreamHandler.java
@@ -60,7 +60,7 @@ public class IotMqttUpstreamHandler extends IotMqttAbstractHandler { @@ -60,7 +60,7 @@ public class IotMqttUpstreamHandler extends IotMqttAbstractHandler {
60 Assert.equals(deviceName, connectionInfo.getDeviceName(), "设备名称不匹配"); 60 Assert.equals(deviceName, connectionInfo.getDeviceName(), "设备名称不匹配");
61 61
62 // 2. 反序列化消息 62 // 2. 反序列化消息
63 - message = deviceMessageService.decodeDeviceMessage(payload, productKey, deviceName); 63 + message = deviceMessageService.deserializeDeviceMessage(payload, productKey, deviceName);
64 if (message == null) { 64 if (message == null) {
65 log.warn("[handleBusinessRequest][消息解码失败,客户端 ID: {},主题: {}]", clientId, topic); 65 log.warn("[handleBusinessRequest][消息解码失败,客户端 ID: {},主题: {}]", clientId, topic);
66 sendErrorResponse(endpoint, productKey, deviceName, null, null, 66 sendErrorResponse(endpoint, productKey, deviceName, null, null,
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/package-info.java
1 /** 1 /**
2 - * 提供设备接入的各种协议的实现 2 + * 设备接入协议:MQTT、EMQX、HTTP、TCP 等协议的实现
3 */ 3 */
4 -package cn.iocoder.yudao.module.iot.gateway.protocol;  
5 \ No newline at end of file 4 \ No newline at end of file
  5 +package cn.iocoder.yudao.module.iot.gateway.protocol;
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/serialize/package-info.java 0 → 100644
  1 +/**
  2 + * 消息序列化:将设备消息转换为字节数组(JSON、二进制等格式)
  3 + *
  4 + * @see cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer
  5 + */
  6 +package cn.iocoder.yudao.module.iot.gateway.serialize;
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/message/IotDeviceMessageService.java
1 package cn.iocoder.yudao.module.iot.gateway.service.device.message; 1 package cn.iocoder.yudao.module.iot.gateway.service.device.message;
2 2
  3 +import cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;
3 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 4 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
4 5
5 /** 6 /**
@@ -10,45 +11,45 @@ import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; @@ -10,45 +11,45 @@ import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
10 public interface IotDeviceMessageService { 11 public interface IotDeviceMessageService {
11 12
12 /** 13 /**
13 - * 编码消息 14 + * 序列化消息
14 * 15 *
15 * @param message 消息 16 * @param message 消息
16 * @param productKey 产品 Key 17 * @param productKey 产品 Key
17 * @param deviceName 设备名称 18 * @param deviceName 设备名称
18 - * @return 编码后的消息内容 19 + * @return 序列化后的消息内容
19 */ 20 */
20 - byte[] encodeDeviceMessage(IotDeviceMessage message,  
21 - String productKey, String deviceName); 21 + byte[] serializeDeviceMessage(IotDeviceMessage message,
  22 + String productKey, String deviceName);
22 23
23 /** 24 /**
24 - * 编码消息 25 + * 序列化消息
25 * 26 *
26 - * @param message 消息  
27 - * @param codecType 编解码器类型  
28 - * @return 编码后的消息内容 27 + * @param message 消息
  28 + * @param serializeType 序列化类型
  29 + * @return 序列化后的消息内容
29 */ 30 */
30 - byte[] encodeDeviceMessage(IotDeviceMessage message,  
31 - String codecType); 31 + byte[] serializeDeviceMessage(IotDeviceMessage message,
  32 + IotSerializeTypeEnum serializeType);
32 33
33 /** 34 /**
34 - * 解码消息 35 + * 反序列化消息
35 * 36 *
36 * @param bytes 消息内容 37 * @param bytes 消息内容
37 * @param productKey 产品 Key 38 * @param productKey 产品 Key
38 * @param deviceName 设备名称 39 * @param deviceName 设备名称
39 - * @return 解码后的消息内容 40 + * @return 反序列化后的消息内容
40 */ 41 */
41 - IotDeviceMessage decodeDeviceMessage(byte[] bytes,  
42 - String productKey, String deviceName); 42 + IotDeviceMessage deserializeDeviceMessage(byte[] bytes,
  43 + String productKey, String deviceName);
43 44
44 /** 45 /**
45 - * 解码消息 46 + * 反序列化消息
46 * 47 *
47 - * @param bytes 消息内容  
48 - * @param codecType 编解码器类型  
49 - * @return 解码后的消息内容 48 + * @param bytes 消息内容
  49 + * @param serializeType 序列化类型
  50 + * @return 反序列化后的消息内容
50 */ 51 */
51 - IotDeviceMessage decodeDeviceMessage(byte[] bytes, String codecType); 52 + IotDeviceMessage deserializeDeviceMessage(byte[] bytes, IotSerializeTypeEnum serializeType);
52 53
53 /** 54 /**
54 * 发送消息 55 * 发送消息
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/message/IotDeviceMessageServiceImpl.java
1 package cn.iocoder.yudao.module.iot.gateway.service.device.message; 1 package cn.iocoder.yudao.module.iot.gateway.service.device.message;
2 2
  3 +import cn.hutool.core.lang.Assert;
3 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
4 -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;  
5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO; 5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
  6 +import cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;
6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 7 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
7 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; 8 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
8 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; 9 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
9 -import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec; 10 +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;
  11 +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializerManager;
10 import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService; 12 import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;
11 import jakarta.annotation.Resource; 13 import jakarta.annotation.Resource;
12 import lombok.extern.slf4j.Slf4j; 14 import lombok.extern.slf4j.Slf4j;
13 import org.springframework.stereotype.Service; 15 import org.springframework.stereotype.Service;
14 16
15 import java.time.LocalDateTime; 17 import java.time.LocalDateTime;
16 -import java.util.List;  
17 -import java.util.Map;  
18 18
19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
20 import static cn.iocoder.yudao.module.iot.gateway.enums.ErrorCodeConstants.DEVICE_NOT_EXISTS; 20 import static cn.iocoder.yudao.module.iot.gateway.enums.ErrorCodeConstants.DEVICE_NOT_EXISTS;
@@ -28,80 +28,70 @@ import static cn.iocoder.yudao.module.iot.gateway.enums.ErrorCodeConstants.DEVIC @@ -28,80 +28,70 @@ import static cn.iocoder.yudao.module.iot.gateway.enums.ErrorCodeConstants.DEVIC
28 @Slf4j 28 @Slf4j
29 public class IotDeviceMessageServiceImpl implements IotDeviceMessageService { 29 public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
30 30
31 - /**  
32 - * 编解码器  
33 - */  
34 - private final Map<String, IotDeviceMessageCodec> codes;  
35 -  
36 @Resource 31 @Resource
37 private IotDeviceService deviceService; 32 private IotDeviceService deviceService;
38 33
39 @Resource 34 @Resource
40 private IotDeviceMessageProducer deviceMessageProducer; 35 private IotDeviceMessageProducer deviceMessageProducer;
41 36
42 - public IotDeviceMessageServiceImpl(List<IotDeviceMessageCodec> codes) {  
43 - this.codes = CollectionUtils.convertMap(codes, IotDeviceMessageCodec::type);  
44 - } 37 + @Resource
  38 + private IotMessageSerializerManager messageSerializerManager;
45 39
46 @Override 40 @Override
47 - public byte[] encodeDeviceMessage(IotDeviceMessage message,  
48 - String productKey, String deviceName) { 41 + public byte[] serializeDeviceMessage(IotDeviceMessage message,
  42 + String productKey, String deviceName) {
49 // 1.1 获取设备信息 43 // 1.1 获取设备信息
50 IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName); 44 IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName);
51 if (device == null) { 45 if (device == null) {
52 throw exception(DEVICE_NOT_EXISTS, productKey, deviceName); 46 throw exception(DEVICE_NOT_EXISTS, productKey, deviceName);
53 } 47 }
54 - // 1.2 获取编解码器  
55 - IotDeviceMessageCodec codec = codes.get(device.getCodecType());  
56 - if (codec == null) {  
57 - throw new IllegalArgumentException(StrUtil.format("编解码器({}) 不存在", device.getCodecType()));  
58 - } 48 + // 1.2 获取序列化器
  49 + IotSerializeTypeEnum serializeType = IotSerializeTypeEnum.of(device.getSerializeType());
  50 + Assert.notNull(serializeType, "设备序列化类型不能为空");
59 51
60 - // 2. 编码消息  
61 - return codec.encode(message); 52 + // 2. 序列化消息
  53 + return serializeDeviceMessage(message, serializeType);
62 } 54 }
63 55
64 @Override 56 @Override
65 - public byte[] encodeDeviceMessage(IotDeviceMessage message,  
66 - String codecType) {  
67 - // 1. 获取编解码器  
68 - IotDeviceMessageCodec codec = codes.get(codecType);  
69 - if (codec == null) {  
70 - throw new IllegalArgumentException(StrUtil.format("编解码器({}) 不存在", codecType)); 57 + public byte[] serializeDeviceMessage(IotDeviceMessage message,
  58 + IotSerializeTypeEnum serializeType) {
  59 + // 1. 获取序列化器
  60 + IotMessageSerializer serializer = messageSerializerManager.get(serializeType);
  61 + if (serializer == null) {
  62 + throw new IllegalArgumentException(StrUtil.format("序列化器({}) 不存在", serializeType));
71 } 63 }
72 64
73 - // 2. 编码消息  
74 - return codec.encode(message); 65 + // 2. 序列化消息
  66 + return serializer.serialize(message);
75 } 67 }
76 68
77 @Override 69 @Override
78 - public IotDeviceMessage decodeDeviceMessage(byte[] bytes,  
79 - String productKey, String deviceName) { 70 + public IotDeviceMessage deserializeDeviceMessage(byte[] bytes,
  71 + String productKey, String deviceName) {
80 // 1.1 获取设备信息 72 // 1.1 获取设备信息
81 IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName); 73 IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName);
82 if (device == null) { 74 if (device == null) {
83 throw exception(DEVICE_NOT_EXISTS, productKey, deviceName); 75 throw exception(DEVICE_NOT_EXISTS, productKey, deviceName);
84 } 76 }
85 - // 1.2 获取编解码器  
86 - IotDeviceMessageCodec codec = codes.get(device.getCodecType());  
87 - if (codec == null) {  
88 - throw new IllegalArgumentException(StrUtil.format("编解码器({}) 不存在", device.getCodecType()));  
89 - } 77 + // 1.2 获取序列化器
  78 + IotSerializeTypeEnum serializeType = IotSerializeTypeEnum.of(device.getSerializeType());
  79 + Assert.notNull(serializeType, "设备序列化类型不能为空");
90 80
91 - // 2. 解码消息  
92 - return codec.decode(bytes); 81 + // 2. 反序列化消息
  82 + return deserializeDeviceMessage(bytes, serializeType);
93 } 83 }
94 84
95 @Override 85 @Override
96 - public IotDeviceMessage decodeDeviceMessage(byte[] bytes, String codecType) {  
97 - // 1. 获取编解码器  
98 - IotDeviceMessageCodec codec = codes.get(codecType);  
99 - if (codec == null) {  
100 - throw new IllegalArgumentException(StrUtil.format("编解码器({}) 不存在", codecType)); 86 + public IotDeviceMessage deserializeDeviceMessage(byte[] bytes, IotSerializeTypeEnum serializeType) {
  87 + // 1. 获取序列化器
  88 + IotMessageSerializer serializer = messageSerializerManager.get(serializeType);
  89 + if (serializer == null) {
  90 + throw new IllegalArgumentException(StrUtil.format("序列化器({}) 不存在", serializeType));
101 } 91 }
102 92
103 - // 2. 解码消息  
104 - return codec.decode(bytes); 93 + // 2. 反序列化消息
  94 + return serializer.deserialize(bytes);
105 } 95 }
106 96
107 @Override 97 @Override
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotDirectDeviceMqttProtocolIntegrationTest.java
@@ -8,8 +8,8 @@ import cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO; @@ -8,8 +8,8 @@ import cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;
8 import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO; 8 import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;
9 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 9 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
10 import cn.iocoder.yudao.module.iot.core.util.IotProductAuthUtils; 10 import cn.iocoder.yudao.module.iot.core.util.IotProductAuthUtils;
11 -import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec;  
12 -import cn.iocoder.yudao.module.iot.gateway.codec.alink.IotAlinkDeviceMessageCodec; 11 +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;
  12 +import cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer;
13 import io.netty.handler.codec.mqtt.MqttQoS; 13 import io.netty.handler.codec.mqtt.MqttQoS;
14 import io.vertx.core.Vertx; 14 import io.vertx.core.Vertx;
15 import io.vertx.core.buffer.Buffer; 15 import io.vertx.core.buffer.Buffer;
@@ -57,9 +57,9 @@ public class IotDirectDeviceMqttProtocolIntegrationTest { @@ -57,9 +57,9 @@ public class IotDirectDeviceMqttProtocolIntegrationTest {
57 57
58 private static Vertx vertx; 58 private static Vertx vertx;
59 59
60 - // ===================== 编解码器(MQTT 使用 Alink 协议) ===================== 60 + // ===================== 序列化器 =====================
61 61
62 - private static final IotDeviceMessageCodec CODEC = new IotAlinkDeviceMessageCodec(); 62 + private static final IotMessageSerializer SERIALIZER = new IotJsonSerializer();
63 63
64 // ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询) ===================== 64 // ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询) =====================
65 65
@@ -211,7 +211,7 @@ public class IotDirectDeviceMqttProtocolIntegrationTest { @@ -211,7 +211,7 @@ public class IotDirectDeviceMqttProtocolIntegrationTest {
211 client.publishHandler(message -> { 211 client.publishHandler(message -> {
212 log.info("[testDeviceRegister][收到响应: topic={}, payload={}]", 212 log.info("[testDeviceRegister][收到响应: topic={}, payload={}]",
213 message.topicName(), message.payload().toString()); 213 message.topicName(), message.payload().toString());
214 - IotDeviceMessage response = CODEC.decode(message.payload().getBytes()); 214 + IotDeviceMessage response = SERIALIZER.deserialize(message.payload().getBytes());
215 responseFuture.complete(response); 215 responseFuture.complete(response);
216 }); 216 });
217 // 3.2 订阅 _reply 主题 217 // 3.2 订阅 _reply 主题
@@ -314,14 +314,14 @@ public class IotDirectDeviceMqttProtocolIntegrationTest { @@ -314,14 +314,14 @@ public class IotDirectDeviceMqttProtocolIntegrationTest {
314 client.publishHandler(message -> { 314 client.publishHandler(message -> {
315 log.info("[publishAndWaitReply][收到响应: topic={}, payload={}]", 315 log.info("[publishAndWaitReply][收到响应: topic={}, payload={}]",
316 message.topicName(), message.payload().toString()); 316 message.topicName(), message.payload().toString());
317 - IotDeviceMessage response = CODEC.decode(message.payload().getBytes()); 317 + IotDeviceMessage response = SERIALIZER.deserialize(message.payload().getBytes());
318 responseFuture.complete(response); 318 responseFuture.complete(response);
319 }); 319 });
320 320
321 - // 2. 编码并发布消息  
322 - byte[] payload = CODEC.encode(request);  
323 - log.info("[publishAndWaitReply][Codec: {}, 发送消息: topic={}, payload={}]",  
324 - CODEC.type(), topic, new String(payload)); 321 + // 2. 序列化并发布消息
  322 + byte[] payload = SERIALIZER.serialize(request);
  323 + log.info("[publishAndWaitReply][Serializer: {}, 发送消息: topic={}, payload={}]",
  324 + SERIALIZER.getType(), topic, new String(payload));
325 client.publish(topic, Buffer.buffer(payload), MqttQoS.AT_LEAST_ONCE, false, false) 325 client.publish(topic, Buffer.buffer(payload), MqttQoS.AT_LEAST_ONCE, false, false)
326 .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS); 326 .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
327 log.info("[publishAndWaitReply][消息发布成功]"); 327 log.info("[publishAndWaitReply][消息发布成功]");
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotGatewayDeviceMqttProtocolIntegrationTest.java
@@ -12,8 +12,8 @@ import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoAddReqDTO; @@ -12,8 +12,8 @@ import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoAddReqDTO;
12 import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoDeleteReqDTO; 12 import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoDeleteReqDTO;
13 import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetReqDTO; 13 import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetReqDTO;
14 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 14 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
15 -import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec;  
16 -import cn.iocoder.yudao.module.iot.gateway.codec.alink.IotAlinkDeviceMessageCodec; 15 +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;
  16 +import cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer;
17 import io.netty.handler.codec.mqtt.MqttQoS; 17 import io.netty.handler.codec.mqtt.MqttQoS;
18 import io.vertx.core.Vertx; 18 import io.vertx.core.Vertx;
19 import io.vertx.core.buffer.Buffer; 19 import io.vertx.core.buffer.Buffer;
@@ -65,9 +65,9 @@ public class IotGatewayDeviceMqttProtocolIntegrationTest { @@ -65,9 +65,9 @@ public class IotGatewayDeviceMqttProtocolIntegrationTest {
65 65
66 private static Vertx vertx; 66 private static Vertx vertx;
67 67
68 - // ===================== 编解码器(MQTT 使用 Alink 协议) ===================== 68 + // ===================== 序列化器 =====================
69 69
70 - private static final IotDeviceMessageCodec CODEC = new IotAlinkDeviceMessageCodec(); 70 + private static final IotMessageSerializer SERIALIZER = new IotJsonSerializer();
71 71
72 // ===================== 网关设备信息(根据实际情况修改,从 iot_device 表查询网关设备) ===================== 72 // ===================== 网关设备信息(根据实际情况修改,从 iot_device 表查询网关设备) =====================
73 73
@@ -399,14 +399,14 @@ public class IotGatewayDeviceMqttProtocolIntegrationTest { @@ -399,14 +399,14 @@ public class IotGatewayDeviceMqttProtocolIntegrationTest {
399 client.publishHandler(message -> { 399 client.publishHandler(message -> {
400 log.info("[publishAndWaitReply][收到响应: topic={}, payload={}]", 400 log.info("[publishAndWaitReply][收到响应: topic={}, payload={}]",
401 message.topicName(), message.payload().toString()); 401 message.topicName(), message.payload().toString());
402 - IotDeviceMessage response = CODEC.decode(message.payload().getBytes()); 402 + IotDeviceMessage response = SERIALIZER.deserialize(message.payload().getBytes());
403 responseFuture.complete(response); 403 responseFuture.complete(response);
404 }); 404 });
405 405
406 - // 2. 编码并发布消息  
407 - byte[] payload = CODEC.encode(request);  
408 - log.info("[publishAndWaitReply][Codec: {}, 发送消息: topic={}, payload={}]",  
409 - CODEC.type(), topic, new String(payload)); 406 + // 2. 序列化并发布消息
  407 + byte[] payload = SERIALIZER.serialize(request);
  408 + log.info("[publishAndWaitReply][Serializer: {}, 发送消息: topic={}, payload={}]",
  409 + SERIALIZER.getType(), topic, new String(payload));
410 client.publish(topic, Buffer.buffer(payload), MqttQoS.AT_LEAST_ONCE, false, false) 410 client.publish(topic, Buffer.buffer(payload), MqttQoS.AT_LEAST_ONCE, false, false)
411 .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS); 411 .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
412 log.info("[publishAndWaitReply][消息发布成功]"); 412 log.info("[publishAndWaitReply][消息发布成功]");
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/IotGatewaySubDeviceMqttProtocolIntegrationTest.java
@@ -7,8 +7,8 @@ import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; @@ -7,8 +7,8 @@ import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
7 import cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO; 7 import cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;
8 import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO; 8 import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;
9 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 9 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
10 -import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec;  
11 -import cn.iocoder.yudao.module.iot.gateway.codec.alink.IotAlinkDeviceMessageCodec; 10 +import cn.iocoder.yudao.module.iot.gateway.serialize.IotMessageSerializer;
  11 +import cn.iocoder.yudao.module.iot.gateway.serialize.json.IotJsonSerializer;
12 import io.netty.handler.codec.mqtt.MqttQoS; 12 import io.netty.handler.codec.mqtt.MqttQoS;
13 import io.vertx.core.Vertx; 13 import io.vertx.core.Vertx;
14 import io.vertx.core.buffer.Buffer; 14 import io.vertx.core.buffer.Buffer;
@@ -59,9 +59,9 @@ public class IotGatewaySubDeviceMqttProtocolIntegrationTest { @@ -59,9 +59,9 @@ public class IotGatewaySubDeviceMqttProtocolIntegrationTest {
59 59
60 private static Vertx vertx; 60 private static Vertx vertx;
61 61
62 - // ===================== 编解码器(MQTT 使用 Alink 协议) ===================== 62 + // ===================== 序列化器 =====================
63 63
64 - private static final IotDeviceMessageCodec CODEC = new IotAlinkDeviceMessageCodec(); 64 + private static final IotMessageSerializer SERIALIZER = new IotJsonSerializer();
65 65
66 // ===================== 网关子设备信息(根据实际情况修改,从 iot_device 表查询子设备) ===================== 66 // ===================== 网关子设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
67 67
@@ -236,14 +236,14 @@ public class IotGatewaySubDeviceMqttProtocolIntegrationTest { @@ -236,14 +236,14 @@ public class IotGatewaySubDeviceMqttProtocolIntegrationTest {
236 client.publishHandler(message -> { 236 client.publishHandler(message -> {
237 log.info("[publishAndWaitReply][收到响应: topic={}, payload={}]", 237 log.info("[publishAndWaitReply][收到响应: topic={}, payload={}]",
238 message.topicName(), message.payload().toString()); 238 message.topicName(), message.payload().toString());
239 - IotDeviceMessage response = CODEC.decode(message.payload().getBytes()); 239 + IotDeviceMessage response = SERIALIZER.deserialize(message.payload().getBytes());
240 responseFuture.complete(response); 240 responseFuture.complete(response);
241 }); 241 });
242 242
243 - // 2. 编码并发布消息  
244 - byte[] payload = CODEC.encode(request);  
245 - log.info("[publishAndWaitReply][Codec: {}, 发送消息: topic={}, payload={}]",  
246 - CODEC.type(), topic, new String(payload)); 243 + // 2. 序列化并发布消息
  244 + byte[] payload = SERIALIZER.serialize(request);
  245 + log.info("[publishAndWaitReply][Serializer: {}, 发送消息: topic={}, payload={}]",
  246 + SERIALIZER.getType(), topic, new String(payload));
247 client.publish(topic, Buffer.buffer(payload), MqttQoS.AT_LEAST_ONCE, false, false) 247 client.publish(topic, Buffer.buffer(payload), MqttQoS.AT_LEAST_ONCE, false, false)
248 .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS); 248 .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
249 log.info("[publishAndWaitReply][消息发布成功]"); 249 log.info("[publishAndWaitReply][消息发布成功]");