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 58 return success(BeanUtils.toBean(device, IotDeviceRespDTO.class, deviceDTO -> {
59 59 IotProductDO product = productService.getProductFromCache(deviceDTO.getProductId());
60 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 67 @DictFormat(DictTypeConstants.NET_TYPE)
68 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 80 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
76 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 1 package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product;
2 2  
3 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 6 import cn.iocoder.yudao.module.iot.enums.product.IotNetTypeEnum;
5 7 import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum;
6 8 import io.swagger.v3.oas.annotations.media.Schema;
... ... @@ -44,9 +46,15 @@ public class IotProductSaveReqVO {
44 46 @InEnum(value = IotNetTypeEnum.class, message = "联网方式必须是 {value}")
45 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 59 @Schema(description = "是否开启动态注册", example = "false")
52 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 78 */
79 79 private Integer netType;
80 80 /**
81   - * 数据格式(编解码器类型)
  81 + * 协议类型
82 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 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 public class DictTypeConstants {
9 9  
10 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 14 public static final String PRODUCT_STATUS = "iot_product_status";
15 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 34 */
35 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 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 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 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 53 return;
54 54 }
55 55 // 2.2 构建载荷
56   - byte[] payload = deviceMessageService.encodeDeviceMessage(message, deviceInfo.getProductKey(),
  56 + byte[] payload = deviceMessageService.serializeDeviceMessage(message, deviceInfo.getProductKey(),
57 57 deviceInfo.getDeviceName());
58   - // 2.3 发布消息
  58 +
  59 + // 3. 发布消息
59 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 452 IotDeviceIdentity deviceInfo = IotDeviceAuthUtils.parseUsername(username);
453 453 Assert.notNull(deviceInfo, "设备信息不能为空");
454 454 try {
455   - // 1. 构建响应消息
  455 + // 1.1 构建响应消息
456 456 String method = IotDeviceMessageMethodEnum.DEVICE_REGISTER.getMethod();
457 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 462 String replyTopic = IotMqttTopicUtils.buildTopicByMethod(method,
464 463 deviceInfo.getProductKey(), deviceInfo.getDeviceName(), true);
  464 +
  465 + // 2. 构建响应主题,并延迟发布(等待设备连接成功并完成订阅)
465 466 protocol.publishDelayMessage(replyTopic, encodedData, 5000);
466 467 log.info("[sendRegisterResultMessage][发送注册结果: topic={}]", replyTopic);
467 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 1 package cn.iocoder.yudao.module.iot.gateway.protocol.emqx.handler.upstream;
2 2  
  3 +import cn.hutool.core.util.ArrayUtil;
3 4 import cn.hutool.core.util.StrUtil;
4 5 import cn.hutool.extra.spring.SpringUtil;
5 6 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
... ... @@ -34,22 +35,21 @@ public class IotEmqxUpstreamHandler {
34 35 try {
35 36 // 1. 解析主题,一次性获取所有信息
36 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 41 log.warn("[handle][topic({}) 格式不正确,无法解析有效的 productKey 和 deviceName]", topic);
39 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 47 if (message == null) {
48 48 log.warn("[handle][topic({}) payload({}) 消息解码失败]", topic, new String(payload));
49 49 return;
50 50 }
51 51  
52   - // 4. 发送消息到队列
  52 + // 3. 发送消息到队列
53 53 deviceMessageService.sendDeviceMessage(message, productKey, deviceName, serverId);
54 54 } catch (Exception e) {
55 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 43 }
44 44  
45 45 // 2.1 序列化消息
46   - byte[] payload = deviceMessageService.encodeDeviceMessage(message, connectionInfo.getProductKey(),
  46 + byte[] payload = deviceMessageService.serializeDeviceMessage(message, connectionInfo.getProductKey(),
47 47 connectionInfo.getDeviceName());
48 48 Assert.isTrue(payload != null && payload.length > 0, "消息编码结果不能为空");
49 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 21 @RequiredArgsConstructor
22 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 24 protected final IotMqttConnectionManager connectionManager;
31 25 protected final IotDeviceMessageService deviceMessageService;
32 26  
... ... @@ -40,8 +34,9 @@ public abstract class IotMqttAbstractHandler {
40 34 * @param method 方法名
41 35 * @param data 响应数据
42 36 */
  37 + @SuppressWarnings("SameParameterValue")
43 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 40 IotDeviceMessage responseMessage = IotDeviceMessage.replyOf(requestId, method, data, 0, null);
46 41 writeResponse(endpoint, productKey, deviceName, method, responseMessage);
47 42 }
... ... @@ -75,11 +70,12 @@ public abstract class IotMqttAbstractHandler {
75 70 private void writeResponse(MqttEndpoint endpoint, String productKey, String deviceName,
76 71 String method, IotDeviceMessage responseMessage) {
77 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 76 String replyTopic = IotMqttTopicUtils.buildTopicByMethod(method, productKey, deviceName, true);
  77 +
  78 + // 2. 发送响应消息
83 79 endpoint.publish(replyTopic, Buffer.buffer(encodedData), MqttQoS.AT_LEAST_ONCE, false, false);
84 80 log.debug("[writeResponse][发送响应,主题: {},code: {}]", replyTopic, responseMessage.getCode());
85 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 14 import io.vertx.mqtt.MqttEndpoint;
15 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 20 * IoT 网关 MQTT 设备注册处理器:处理设备动态注册消息(一型一密)
19 21 *
... ... @@ -76,7 +78,8 @@ public class IotMqttRegisterHandler extends IotMqttAbstractHandler {
76 78 log.warn("[handleRegister][注册失败,客户端 ID: {},错误: {}]", clientId, e.getMessage());
77 79 // 接受连接,并发送错误响应
78 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 83 } finally {
81 84 // 注册完成后关闭连接(一型一密只用于获取 deviceSecret,不保持连接)
82 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 60 Assert.equals(deviceName, connectionInfo.getDeviceName(), "设备名称不匹配");
61 61  
62 62 // 2. 反序列化消息
63   - message = deviceMessageService.decodeDeviceMessage(payload, productKey, deviceName);
  63 + message = deviceMessageService.deserializeDeviceMessage(payload, productKey, deviceName);
64 64 if (message == null) {
65 65 log.warn("[handleBusinessRequest][消息解码失败,客户端 ID: {},主题: {}]", clientId, topic);
66 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 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 1 package cn.iocoder.yudao.module.iot.gateway.service.device.message;
2 2  
  3 +import cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;
3 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 11 public interface IotDeviceMessageService {
11 12  
12 13 /**
13   - * 编码消息
  14 + * 序列化消息
14 15 *
15 16 * @param message 消息
16 17 * @param productKey 产品 Key
17 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 37 * @param bytes 消息内容
37 38 * @param productKey 产品 Key
38 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 1 package cn.iocoder.yudao.module.iot.gateway.service.device.message;
2 2  
  3 +import cn.hutool.core.lang.Assert;
3 4 import cn.hutool.core.util.StrUtil;
4   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5 5 import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
  6 +import cn.iocoder.yudao.module.iot.core.enums.IotSerializeTypeEnum;
6 7 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
7 8 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
8 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 12 import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;
11 13 import jakarta.annotation.Resource;
12 14 import lombok.extern.slf4j.Slf4j;
13 15 import org.springframework.stereotype.Service;
14 16  
15 17 import java.time.LocalDateTime;
16   -import java.util.List;
17   -import java.util.Map;
18 18  
19 19 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
20 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 28 @Slf4j
29 29 public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
30 30  
31   - /**
32   - * 编解码器
33   - */
34   - private final Map<String, IotDeviceMessageCodec> codes;
35   -
36 31 @Resource
37 32 private IotDeviceService deviceService;
38 33  
39 34 @Resource
40 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 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 43 // 1.1 获取设备信息
50 44 IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName);
51 45 if (device == null) {
52 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 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 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 72 // 1.1 获取设备信息
81 73 IotDeviceRespDTO device = deviceService.getDeviceFromCache(productKey, deviceName);
82 74 if (device == null) {
83 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 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 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 import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;
9 9 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
10 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 13 import io.netty.handler.codec.mqtt.MqttQoS;
14 14 import io.vertx.core.Vertx;
15 15 import io.vertx.core.buffer.Buffer;
... ... @@ -57,9 +57,9 @@ public class IotDirectDeviceMqttProtocolIntegrationTest {
57 57  
58 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 64 // ===================== 直连设备信息(根据实际情况修改,从 iot_device 表查询) =====================
65 65  
... ... @@ -211,7 +211,7 @@ public class IotDirectDeviceMqttProtocolIntegrationTest {
211 211 client.publishHandler(message -> {
212 212 log.info("[testDeviceRegister][收到响应: topic={}, payload={}]",
213 213 message.topicName(), message.payload().toString());
214   - IotDeviceMessage response = CODEC.decode(message.payload().getBytes());
  214 + IotDeviceMessage response = SERIALIZER.deserialize(message.payload().getBytes());
215 215 responseFuture.complete(response);
216 216 });
217 217 // 3.2 订阅 _reply 主题
... ... @@ -314,14 +314,14 @@ public class IotDirectDeviceMqttProtocolIntegrationTest {
314 314 client.publishHandler(message -> {
315 315 log.info("[publishAndWaitReply][收到响应: topic={}, payload={}]",
316 316 message.topicName(), message.payload().toString());
317   - IotDeviceMessage response = CODEC.decode(message.payload().getBytes());
  317 + IotDeviceMessage response = SERIALIZER.deserialize(message.payload().getBytes());
318 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 325 client.publish(topic, Buffer.buffer(payload), MqttQoS.AT_LEAST_ONCE, false, false)
326 326 .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
327 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 12 import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoDeleteReqDTO;
13 13 import cn.iocoder.yudao.module.iot.core.topic.topo.IotDeviceTopoGetReqDTO;
14 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 17 import io.netty.handler.codec.mqtt.MqttQoS;
18 18 import io.vertx.core.Vertx;
19 19 import io.vertx.core.buffer.Buffer;
... ... @@ -65,9 +65,9 @@ public class IotGatewayDeviceMqttProtocolIntegrationTest {
65 65  
66 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 72 // ===================== 网关设备信息(根据实际情况修改,从 iot_device 表查询网关设备) =====================
73 73  
... ... @@ -399,14 +399,14 @@ public class IotGatewayDeviceMqttProtocolIntegrationTest {
399 399 client.publishHandler(message -> {
400 400 log.info("[publishAndWaitReply][收到响应: topic={}, payload={}]",
401 401 message.topicName(), message.payload().toString());
402   - IotDeviceMessage response = CODEC.decode(message.payload().getBytes());
  402 + IotDeviceMessage response = SERIALIZER.deserialize(message.payload().getBytes());
403 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 410 client.publish(topic, Buffer.buffer(payload), MqttQoS.AT_LEAST_ONCE, false, false)
411 411 .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
412 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 7 import cn.iocoder.yudao.module.iot.core.topic.event.IotDeviceEventPostReqDTO;
8 8 import cn.iocoder.yudao.module.iot.core.topic.property.IotDevicePropertyPostReqDTO;
9 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 12 import io.netty.handler.codec.mqtt.MqttQoS;
13 13 import io.vertx.core.Vertx;
14 14 import io.vertx.core.buffer.Buffer;
... ... @@ -59,9 +59,9 @@ public class IotGatewaySubDeviceMqttProtocolIntegrationTest {
59 59  
60 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 66 // ===================== 网关子设备信息(根据实际情况修改,从 iot_device 表查询子设备) =====================
67 67  
... ... @@ -236,14 +236,14 @@ public class IotGatewaySubDeviceMqttProtocolIntegrationTest {
236 236 client.publishHandler(message -> {
237 237 log.info("[publishAndWaitReply][收到响应: topic={}, payload={}]",
238 238 message.topicName(), message.payload().toString());
239   - IotDeviceMessage response = CODEC.decode(message.payload().getBytes());
  239 + IotDeviceMessage response = SERIALIZER.deserialize(message.payload().getBytes());
240 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 247 client.publish(topic, Buffer.buffer(payload), MqttQoS.AT_LEAST_ONCE, false, false)
248 248 .toCompletionStage().toCompletableFuture().get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
249 249 log.info("[publishAndWaitReply][消息发布成功]");
... ...