Commit 120029bb1793737d6e0c8d429d7c2d43eb925fe6

Authored by YunaiV
1 parent f58cf282

reactor:【IoT 物联网】实现 alink 的 IotAlinkDeviceMessageCodec,并尝试接入 http 协议

Showing 14 changed files with 388 additions and 133 deletions
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/enums/IotDeviceMessageMethodEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.core.enums;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * IoT 设备消息的方法枚举
  8 + *
  9 + * @author haohao
  10 + */
  11 +@Getter
  12 +@AllArgsConstructor
  13 +public enum IotDeviceMessageMethodEnum {
  14 +
  15 + // ========== 设备状态 ==========
  16 +
  17 + STATE_ONLINE("thing.state.online"),
  18 + STATE_OFFLINE("thing.state.offline"),
  19 +
  20 + ;
  21 +
  22 + private final String method;
  23 +
  24 +
  25 +}
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/mq/message/IotDeviceMessage.java
1 package cn.iocoder.yudao.module.iot.core.mq.message; 1 package cn.iocoder.yudao.module.iot.core.mq.message;
2 2
3 -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageIdentifierEnum;  
4 -import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageTypeEnum; 3 +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
5 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils; 4 import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
6 import lombok.AllArgsConstructor; 5 import lombok.AllArgsConstructor;
7 import lombok.Builder; 6 import lombok.Builder;
@@ -9,7 +8,6 @@ import lombok.Data; @@ -9,7 +8,6 @@ import lombok.Data;
9 import lombok.NoArgsConstructor; 8 import lombok.NoArgsConstructor;
10 9
11 import java.time.LocalDateTime; 10 import java.time.LocalDateTime;
12 -import java.util.Map;  
13 11
14 /** 12 /**
15 * IoT 设备消息 13 * IoT 设备消息
@@ -32,109 +30,126 @@ public class IotDeviceMessage { @@ -32,109 +30,126 @@ public class IotDeviceMessage {
32 */ 30 */
33 public static final String MESSAGE_BUS_GATEWAY_DEVICE_MESSAGE_TOPIC = MESSAGE_BUS_DEVICE_MESSAGE_TOPIC + "_%s"; 31 public static final String MESSAGE_BUS_GATEWAY_DEVICE_MESSAGE_TOPIC = MESSAGE_BUS_DEVICE_MESSAGE_TOPIC + "_%s";
34 32
35 - // TODO @芋艿:thingsboard 对应 id,全部由后端生成,由于追溯;是不是调整下?  
36 /** 33 /**
37 * 消息编号 34 * 消息编号
  35 + *
  36 + * 由后端生成,通过 {@link IotDeviceMessageUtils#generateMessageId()}
38 */ 37 */
39 - private String messageId;  
40 -  
41 - // TODO @芋艿:thingsboard 是使用 deviceId  
42 - /**  
43 - * 设备信息  
44 - */  
45 - private String productKey; 38 + private String id;
46 /** 39 /**
47 - * 设备名称 40 + * 上报时间
  41 + *
  42 + * 由后端生成,当前时间
48 */ 43 */
49 - private String deviceName; 44 + private LocalDateTime reportTime;
  45 +
  46 + // ========== codec(编解码)字段 ==========
50 47
51 - // TODO @芋艿:thingsboard 只定义了 type;相当于 type + identifier 结合!TbMsgType  
52 /** 48 /**
53 - * 消息类型 49 + * 请求编号
54 * 50 *
55 - * 枚举 {@link IotDeviceMessageTypeEnum} 51 + * 由设备生成,对应阿里云 IoT 的 Alink 协议中的 id、华为云 IoTDA 协议的 request_id
56 */ 52 */
57 - private String type; 53 + private String requestId;
58 /** 54 /**
59 - * 标识符 55 + * 请求方法
60 * 56 *
61 - * 枚举 {@link IotDeviceMessageIdentifierEnum} 57 + * 枚举 {@link IotDeviceMessageMethodEnum}
  58 + * 例如说:thing.property.report 属性上报
62 */ 59 */
63 - private String identifier;  
64 -  
65 - // TODO @芋艿:thingsboard 只有 data 字段,没有 code 字段;  
66 - // TODO @芋艿:要不提前序列化成字符串???类似 thingsboard 的 data 字段 60 + private String method;
67 /** 61 /**
68 * 请求参数 62 * 请求参数
69 * 63 *
70 * 例如说:属性上报的 properties、事件上报的 params 64 * 例如说:属性上报的 properties、事件上报的 params
71 */ 65 */
  66 + private Object params;
  67 + /**
  68 + * 响应结果
  69 + */
72 private Object data; 70 private Object data;
73 - // TODO @芋艿:可能会去掉  
74 /** 71 /**
75 - * 响应码  
76 - *  
77 - * 目前只有 server 下行消息给 device 设备时,才会有响应码 72 + * 响应错误码
78 */ 73 */
79 private Integer code; 74 private Integer code;
80 75
  76 + // ========== 后端字段 ==========
  77 +
81 /** 78 /**
82 - * 上报时间 79 + * 设备编号
83 */ 80 */
84 - private LocalDateTime reportTime; 81 + private Long deviceId;
  82 + /**
  83 + * 租户编号
  84 + */
  85 + private Long tenantId;
85 86
86 /** 87 /**
87 * 服务编号,该消息由哪个 server 服务进行消费 88 * 服务编号,该消息由哪个 server 服务进行消费
88 */ 89 */
89 private String serverId; 90 private String serverId;
90 91
91 - public IotDeviceMessage ofPropertyReport(Map<String, Object> properties) {  
92 - this.setType(IotDeviceMessageTypeEnum.PROPERTY.getType());  
93 - this.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier());  
94 - this.setData(properties);  
95 - return this;  
96 - }  
97 -  
98 - public IotDeviceMessage ofPropertySet(Map<String, Object> properties) {  
99 - this.setType(IotDeviceMessageTypeEnum.PROPERTY.getType());  
100 - this.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier());  
101 - this.setData(properties);  
102 - return this;  
103 - }  
104 -  
105 - public IotDeviceMessage ofStateOnline() {  
106 - this.setType(IotDeviceMessageTypeEnum.STATE.getType());  
107 - this.setIdentifier(IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier());  
108 - return this;  
109 - }  
110 -  
111 - public IotDeviceMessage ofStateOffline() {  
112 - this.setType(IotDeviceMessageTypeEnum.STATE.getType());  
113 - this.setIdentifier(IotDeviceMessageIdentifierEnum.STATE_OFFLINE.getIdentifier());  
114 - return this;  
115 - }  
116 -  
117 - public static IotDeviceMessage of(String productKey, String deviceName) {  
118 - return of(productKey, deviceName,  
119 - null, null);  
120 - }  
121 -  
122 - public static IotDeviceMessage of(String productKey, String deviceName,  
123 - String serverId) {  
124 - return of(productKey, deviceName,  
125 - null, serverId); 92 +// public IotDeviceMessage ofPropertyReport(Map<String, Object> properties) {
  93 +// this.setType(IotDeviceMessageTypeEnum.PROPERTY.getType());
  94 +// this.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_REPORT.getIdentifier());
  95 +// this.setData(properties);
  96 +// return this;
  97 +// }
  98 +//
  99 +// public IotDeviceMessage ofPropertySet(Map<String, Object> properties) {
  100 +// this.setType(IotDeviceMessageTypeEnum.PROPERTY.getType());
  101 +// this.setIdentifier(IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier());
  102 +// this.setData(properties);
  103 +// return this;
  104 +// }
  105 +//
  106 +// public IotDeviceMessage ofStateOnline() {
  107 +// this.setType(IotDeviceMessageTypeEnum.STATE.getType());
  108 +// this.setIdentifier(IotDeviceMessageIdentifierEnum.STATE_ONLINE.getIdentifier());
  109 +// return this;
  110 +// }
  111 +//
  112 +// public IotDeviceMessage ofStateOffline() {
  113 +// this.setType(IotDeviceMessageTypeEnum.STATE.getType());
  114 +// this.setIdentifier(IotDeviceMessageIdentifierEnum.STATE_OFFLINE.getIdentifier());
  115 +// return this;
  116 +// }
  117 +//
  118 +// public static IotDeviceMessage of(String productKey, String deviceName) {
  119 +// return of(productKey, deviceName,
  120 +// null, null);
  121 +// }
  122 +//
  123 +// public static IotDeviceMessage of(String productKey, String deviceName,
  124 +// String serverId) {
  125 +// return of(productKey, deviceName,
  126 +// null, serverId);
  127 +// }
  128 +//
  129 +// public static IotDeviceMessage of(String productKey, String deviceName,
  130 +// LocalDateTime reportTime, String serverId) {
  131 +// if (reportTime == null) {
  132 +// reportTime = LocalDateTime.now();
  133 +// }
  134 +// String messageId = IotDeviceMessageUtils.generateMessageId();
  135 +// return IotDeviceMessage.builder()
  136 +// .messageId(messageId).reportTime(reportTime)
  137 +// .productKey(productKey).deviceName(deviceName)
  138 +// .serverId(serverId).build();
  139 +// }
  140 +
  141 + public static IotDeviceMessage of(String requestId, String method, Object params) {
  142 + return of(requestId, method, params, null, null);
126 } 143 }
127 144
128 - public static IotDeviceMessage of(String productKey, String deviceName,  
129 - LocalDateTime reportTime, String serverId) {  
130 - if (reportTime == null) {  
131 - reportTime = LocalDateTime.now();  
132 - }  
133 - String messageId = IotDeviceMessageUtils.generateMessageId();  
134 - return IotDeviceMessage.builder()  
135 - .messageId(messageId).reportTime(reportTime)  
136 - .productKey(productKey).deviceName(deviceName)  
137 - .serverId(serverId).build(); 145 + public static IotDeviceMessage of(String requestId, String method,
  146 + Object params, Object data, Integer code) {
  147 + // 通用参数
  148 + IotDeviceMessage message = new IotDeviceMessage()
  149 + .setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now());
  150 + // 当前参数
  151 + message.setRequestId(requestId).setMethod(method).setParams(params).setData(data).setCode(code);
  152 + return message;
138 } 153 }
139 154
140 } 155 }
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/mq/producer/IotDeviceMessageProducer.java
@@ -30,7 +30,7 @@ public class IotDeviceMessageProducer { @@ -30,7 +30,7 @@ public class IotDeviceMessageProducer {
30 * @param serverId 网关的 serverId 标识 30 * @param serverId 网关的 serverId 标识
31 * @param message 设备消息 31 * @param message 设备消息
32 */ 32 */
33 - public void sendGatewayDeviceMessage(String serverId, Object message) { 33 + public void sendDeviceMessageToGateway(String serverId, IotDeviceMessage message) {
34 messageBus.post(IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(serverId), message); 34 messageBus.post(IotDeviceMessageUtils.buildMessageBusGatewayDeviceMessageTopic(serverId), message);
35 } 35 }
36 36
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtils.java
@@ -18,6 +18,7 @@ public class IotDeviceMessageUtils { @@ -18,6 +18,7 @@ public class IotDeviceMessageUtils {
18 return IdUtil.fastSimpleUUID(); 18 return IdUtil.fastSimpleUUID();
19 } 19 }
20 20
  21 + // TODO @芋艿:需要优化下;
21 /** 22 /**
22 * 是否是上行消息:由设备发送 23 * 是否是上行消息:由设备发送
23 * 24 *
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/IotDeviceMessageCodec.java 0 → 100644
  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 0 → 100644
  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.util.json.JsonUtils;
  5 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  6 +import cn.iocoder.yudao.module.iot.gateway.codec.IotDeviceMessageCodec;
  7 +import lombok.AllArgsConstructor;
  8 +import lombok.Data;
  9 +import lombok.NoArgsConstructor;
  10 +import org.springframework.stereotype.Component;
  11 +
  12 +/**
  13 + * 阿里云 Alink {@link IotDeviceMessage} 的编解码器
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Component
  18 +public class IotAlinkDeviceMessageCodec implements IotDeviceMessageCodec {
  19 +
  20 + @Data
  21 + @NoArgsConstructor
  22 + @AllArgsConstructor
  23 + private static class AlinkMessage {
  24 +
  25 + public static final String VERSION_1 = "1.0";
  26 +
  27 + /**
  28 + * 消息 ID,且每个消息 ID 在当前设备具有唯一性
  29 + */
  30 + private String id;
  31 +
  32 + /**
  33 + * 版本号
  34 + */
  35 + private String version;
  36 +
  37 + /**
  38 + * 请求方法
  39 + */
  40 + private String method;
  41 +
  42 + /**
  43 + * 请求参数
  44 + */
  45 + private Object params;
  46 +
  47 + /**
  48 + * 响应结果
  49 + */
  50 + private Object data;
  51 +
  52 + /**
  53 + * 响应错误码
  54 + */
  55 + private Integer code;
  56 +
  57 + }
  58 +
  59 + @Override
  60 + public byte[] encode(IotDeviceMessage message) {
  61 + AlinkMessage alinkMessage = new AlinkMessage(message.getRequestId(), AlinkMessage.VERSION_1,
  62 + message.getMethod(), message.getParams(), message.getData(), message.getCode());
  63 + return JsonUtils.toJsonByte(alinkMessage);
  64 + }
  65 +
  66 + @Override
  67 + @SuppressWarnings("DataFlowIssue")
  68 + public IotDeviceMessage decode(byte[] bytes) {
  69 + AlinkMessage alinkMessage = JsonUtils.parseObject(bytes, AlinkMessage.class);
  70 + Assert.notNull(alinkMessage, "消息不能为空");
  71 + Assert.equals(alinkMessage.getVersion(), AlinkMessage.VERSION_1, "消息版本号必须是 1.0");
  72 + return IotDeviceMessage.of(alinkMessage.getId(),
  73 + alinkMessage.getMethod(), alinkMessage.getParams(), alinkMessage.getData(), alinkMessage.getCode());
  74 + }
  75 +
  76 + @Override
  77 + public String type() {
  78 + return "alink";
  79 + }
  80 +
  81 +}
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/codec/alink/package-info.java deleted
1 -package cn.iocoder.yudao.module.iot.gateway.codec.alink;  
2 \ 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/protocol/http/router/IotHttpAbstractHandler.java
@@ -33,7 +33,7 @@ public abstract class IotHttpAbstractHandler implements Handler&lt;RoutingContext&gt; @@ -33,7 +33,7 @@ public abstract class IotHttpAbstractHandler implements Handler&lt;RoutingContext&gt;
33 private final IotDeviceTokenService deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class); 33 private final IotDeviceTokenService deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class);
34 34
35 @Override 35 @Override
36 - public void handle(RoutingContext context) { 36 + public final void handle(RoutingContext context) {
37 try { 37 try {
38 // 1. 前置处理 38 // 1. 前置处理
39 CommonResult<Object> result = beforeHandle(context); 39 CommonResult<Object> result = beforeHandle(context);
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpAuthHandler.java
@@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; @@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
13 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 13 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
14 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; 14 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol;
15 import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService; 15 import cn.iocoder.yudao.module.iot.gateway.service.auth.IotDeviceTokenService;
  16 +import cn.iocoder.yudao.module.iot.gateway.service.message.IotDeviceMessageService;
16 import io.vertx.core.json.JsonObject; 17 import io.vertx.core.json.JsonObject;
17 import io.vertx.ext.web.RoutingContext; 18 import io.vertx.ext.web.RoutingContext;
18 19
@@ -40,11 +41,14 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { @@ -40,11 +41,14 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler {
40 41
41 private final IotDeviceCommonApi deviceClientService; 42 private final IotDeviceCommonApi deviceClientService;
42 43
  44 + private final IotDeviceMessageService deviceMessageService;
  45 +
43 public IotHttpAuthHandler(IotHttpUpstreamProtocol protocol) { 46 public IotHttpAuthHandler(IotHttpUpstreamProtocol protocol) {
44 this.protocol = protocol; 47 this.protocol = protocol;
45 this.deviceMessageProducer = SpringUtil.getBean(IotDeviceMessageProducer.class); 48 this.deviceMessageProducer = SpringUtil.getBean(IotDeviceMessageProducer.class);
46 this.deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class); 49 this.deviceTokenService = SpringUtil.getBean(IotDeviceTokenService.class);
47 this.deviceClientService = SpringUtil.getBean(IotDeviceCommonApi.class); 50 this.deviceClientService = SpringUtil.getBean(IotDeviceCommonApi.class);
  51 + this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);
48 } 52 }
49 53
50 @Override 54 @Override
@@ -78,9 +82,9 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler { @@ -78,9 +82,9 @@ public class IotHttpAuthHandler extends IotHttpAbstractHandler {
78 Assert.notBlank(token, "生成 token 不能为空位"); 82 Assert.notBlank(token, "生成 token 不能为空位");
79 83
80 // 3. 执行上线 84 // 3. 执行上线
81 - deviceMessageProducer.sendDeviceMessage(IotDeviceMessage.of(deviceInfo.getProductKey(), deviceInfo.getDeviceName(),  
82 - protocol.getServerId())  
83 - .ofStateOnline()); 85 + IotDeviceMessage message = deviceMessageService.buildDeviceMessageOfStateOnline(
  86 + deviceInfo.getProductKey(), deviceInfo.getDeviceName(), protocol.getServerId());
  87 + deviceMessageProducer.sendDeviceMessage(message);
84 88
85 // 构建响应数据 89 // 构建响应数据
86 return success(MapUtil.of("token", token)); 90 return success(MapUtil.of("token", token));
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/http/router/IotHttpUpstreamHandler.java
1 package cn.iocoder.yudao.module.iot.gateway.protocol.http.router; 1 package cn.iocoder.yudao.module.iot.gateway.protocol.http.router;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
4 import cn.hutool.core.map.MapUtil; 5 import cn.hutool.core.map.MapUtil;
  6 +import cn.hutool.core.text.StrPool;
5 import cn.hutool.core.util.StrUtil; 7 import cn.hutool.core.util.StrUtil;
6 import cn.hutool.extra.spring.SpringUtil; 8 import cn.hutool.extra.spring.SpringUtil;
  9 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
7 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; 10 import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
8 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer; 11 import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
9 import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum; 12 import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum;
10 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol; 13 import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol;
11 -import io.vertx.core.Handler; 14 +import cn.iocoder.yudao.module.iot.gateway.service.message.IotDeviceMessageService;
12 import io.vertx.core.json.JsonObject; 15 import io.vertx.core.json.JsonObject;
13 import io.vertx.ext.web.RoutingContext; 16 import io.vertx.ext.web.RoutingContext;
14 import lombok.RequiredArgsConstructor; 17 import lombok.RequiredArgsConstructor;
@@ -23,49 +26,39 @@ import java.util.Map; @@ -23,49 +26,39 @@ import java.util.Map;
23 */ 26 */
24 @RequiredArgsConstructor 27 @RequiredArgsConstructor
25 @Slf4j 28 @Slf4j
26 -public class IotHttpUpstreamHandler implements Handler<RoutingContext> { 29 +public class IotHttpUpstreamHandler extends IotHttpAbstractHandler {
27 30
28 - // TODO @haohao:你说,咱要不要把 "/sys/:productKey/:deviceName"  
29 - // + IotDeviceTopicEnum.PROPERTY_POST_TOPIC.getTopic(),也抽到 IotDeviceTopicEnum 的 build 这种?尽量都收敛掉?  
30 - /**  
31 - * 属性上报路径  
32 - */  
33 - public static final String PROPERTY_PATH = "/sys/:productKey/:deviceName"  
34 - + IotDeviceTopicEnum.PROPERTY_POST_TOPIC.getTopic();  
35 -  
36 - /**  
37 - * 事件上报路径  
38 - */  
39 - public static final String EVENT_PATH = "/sys/:productKey/:deviceName"  
40 - + IotDeviceTopicEnum.EVENT_POST_TOPIC_PREFIX.getTopic() + ":identifier"  
41 - + IotDeviceTopicEnum.EVENT_POST_TOPIC_SUFFIX.getTopic(); 31 + public static final String PATH = "/topic/sys/:productKey/:deviceName/*";
42 32
43 private final IotHttpUpstreamProtocol protocol; 33 private final IotHttpUpstreamProtocol protocol;
44 34
45 private final IotDeviceMessageProducer deviceMessageProducer; 35 private final IotDeviceMessageProducer deviceMessageProducer;
46 36
  37 + private final IotDeviceMessageService deviceMessageService;
  38 +
47 public IotHttpUpstreamHandler(IotHttpUpstreamProtocol protocol) { 39 public IotHttpUpstreamHandler(IotHttpUpstreamProtocol protocol) {
48 this.protocol = protocol; 40 this.protocol = protocol;
49 this.deviceMessageProducer = SpringUtil.getBean(IotDeviceMessageProducer.class); 41 this.deviceMessageProducer = SpringUtil.getBean(IotDeviceMessageProducer.class);
  42 + this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);
50 } 43 }
51 44
52 @Override 45 @Override
53 - public void handle(RoutingContext context) {  
54 - String path = context.request().path(); 46 + protected CommonResult<Object> handle0(RoutingContext context) {
55 // 1. 解析通用参数 47 // 1. 解析通用参数
56 String productKey = context.pathParam("productKey"); 48 String productKey = context.pathParam("productKey");
57 String deviceName = context.pathParam("deviceName"); 49 String deviceName = context.pathParam("deviceName");
58 - JsonObject body = context.body().asJsonObject();  
59 -  
60 - // 2. 根据路径模式处理不同类型的请求  
61 - if (isPropertyPostPath(path)) {  
62 - // 处理属性上报  
63 - handlePropertyPost(context, productKey, deviceName, body);  
64 - } else if (isEventPostPath(path)) {  
65 - // 处理事件上报  
66 - String identifier = context.pathParam("identifier");  
67 - handleEventPost(context, productKey, deviceName, identifier, body);  
68 - } 50 + String method = context.pathParam("*").replaceAll(StrPool.SLASH, StrPool.DOT);
  51 +
  52 + // 2.1 解析消息
  53 + byte[] bytes = context.body().buffer().getBytes();
  54 + IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(bytes,
  55 + productKey, deviceName, protocol.getServerId());
  56 + Assert.equals(method, message.getMethod(), "method 不匹配");
  57 + // 2.2 发送消息
  58 + deviceMessageProducer.sendDeviceMessage(message);
  59 +
  60 + // 3. 返回结果
  61 + return CommonResult.success(MapUtil.of("messageId", message.getId()));
69 } 62 }
70 63
71 /** 64 /**
@@ -101,7 +94,8 @@ public class IotHttpUpstreamHandler implements Handler&lt;RoutingContext&gt; { @@ -101,7 +94,8 @@ public class IotHttpUpstreamHandler implements Handler&lt;RoutingContext&gt; {
101 JsonObject body) { 94 JsonObject body) {
102 // 1.1 构建设备消息 95 // 1.1 构建设备消息
103 IotDeviceMessage message = IotDeviceMessage.of(productKey, deviceName, protocol.getServerId()) 96 IotDeviceMessage message = IotDeviceMessage.of(productKey, deviceName, protocol.getServerId())
104 - .ofPropertyReport(parsePropertiesFromBody(body)); 97 +// .ofPropertyReport(parsePropertiesFromBody(body))
  98 + ;
105 // 1.2 发送消息 99 // 1.2 发送消息
106 deviceMessageProducer.sendDeviceMessage(message); 100 deviceMessageProducer.sendDeviceMessage(message);
107 101
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/mqtt/router/package-info.java deleted
1 -/**  
2 - * MQTT 协议路由器包  
3 - * <p>  
4 - * 包含 MQTT 协议的所有路由处理器和抽象基类:  
5 - * <ul>  
6 - * <li>{@link cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttAbstractHandler}  
7 - * - 抽象路由处理器基类</li>  
8 - * <li>{@link cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttUpstreamRouter}  
9 - * - 上行消息路由器</li>  
10 - * <li>{@link cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttAuthRouter}  
11 - * - 认证路由器</li>  
12 - * <li>{@link cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttPropertyHandler}  
13 - * - 属性处理器</li>  
14 - * <li>{@link cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttEventHandler}  
15 - * - 事件处理器</li>  
16 - * <li>{@link cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router.IotMqttServiceHandler}  
17 - * - 服务处理器</li>  
18 - * </ul>  
19 - *  
20 - * @author 芋道源码  
21 - */  
22 -package cn.iocoder.yudao.module.iot.gateway.protocol.mqtt.router;  
23 \ 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/service/auth/IotDeviceTokenService.java
@@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.gateway.service.auth; @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.iot.gateway.service.auth;
3 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils; 3 import cn.iocoder.yudao.module.iot.core.util.IotDeviceAuthUtils;
4 4
5 /** 5 /**
6 - * IoT 设备 Token 服务 Service 接口 6 + * IoT 设备 Token Service 接口
7 * 7 *
8 * @author 芋道源码 8 * @author 芋道源码
9 */ 9 */
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/message/IotDeviceMessageService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.service.message;
  2 +
  3 +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
  4 +
  5 +/**
  6 + * IoT 设备消息 Service 接口
  7 + *
  8 + * @author 芋道源码
  9 + */
  10 +public interface IotDeviceMessageService {
  11 +
  12 + /**
  13 + * 编码消息
  14 + *
  15 + * @param message 消息
  16 + * @param productKey 产品 Key
  17 + * @param deviceName 设备名称
  18 + * @return 编码后的消息内容
  19 + */
  20 + byte[] encodeDeviceMessage(IotDeviceMessage message,
  21 + String productKey, String deviceName);
  22 +
  23 + /**
  24 + * 解码消息
  25 + *
  26 + * @param bytes 消息内容
  27 + * @param productKey 产品 Key
  28 + * @param deviceName 设备名称
  29 + * @param serverId 设备连接的 serverId
  30 + * @return 解码后的消息内容
  31 + */
  32 + IotDeviceMessage decodeDeviceMessage(byte[] bytes,
  33 + String productKey, String deviceName, String serverId);
  34 +
  35 + /**
  36 + * 构建【设备上线】消息
  37 + *
  38 + * @return 消息
  39 + */
  40 + IotDeviceMessage buildDeviceMessageOfStateOnline(String productKey, String deviceName, String serverId);
  41 +
  42 +}
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/message/IotDeviceMessageServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.gateway.service.message;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  5 +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
  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.alink.IotAlinkDeviceMessageCodec;
  9 +import org.springframework.stereotype.Service;
  10 +
  11 +import java.time.LocalDateTime;
  12 +import java.util.List;
  13 +import java.util.Map;
  14 +
  15 +/**
  16 + * IoT 设备消息 Service 实现类
  17 + *
  18 + * @author 芋道源码
  19 + */
  20 +@Service
  21 +public class IotDeviceMessageServiceImpl implements IotDeviceMessageService {
  22 +
  23 + /**
  24 + * 编解码器
  25 + */
  26 + private final Map<String, IotAlinkDeviceMessageCodec> codes;
  27 +
  28 + public IotDeviceMessageServiceImpl(List<IotAlinkDeviceMessageCodec> codes) {
  29 + this.codes = CollectionUtils.convertMap(codes, IotAlinkDeviceMessageCodec::type);
  30 + }
  31 +
  32 + @Override
  33 + public byte[] encodeDeviceMessage(IotDeviceMessage message,
  34 + String productKey, String deviceName) {
  35 + // TODO @芋艿:获取设备信息
  36 + String codecType = "alink";
  37 + return codes.get(codecType).encode(message);
  38 + }
  39 +
  40 + @Override
  41 + public IotDeviceMessage decodeDeviceMessage(byte[] bytes,
  42 + String productKey, String deviceName, String serverId) {
  43 + // TODO @芋艿:获取设备信息
  44 + String codecType = "alink";
  45 + IotDeviceMessage message = codes.get(codecType).decode(bytes);
  46 + // 补充后端字段
  47 + Long deviceId = 25L;
  48 + Long tenantId = 1L;
  49 + appendDeviceMessage(message, deviceId, tenantId, serverId);
  50 + return message;
  51 + }
  52 +
  53 + @Override
  54 + public IotDeviceMessage buildDeviceMessageOfStateOnline(String productKey, String deviceName, String serverId) {
  55 + IotDeviceMessage message = IotDeviceMessage.of(null,
  56 + IotDeviceMessageMethodEnum.STATE_ONLINE.getMethod(), null);
  57 + // 补充后端字段
  58 + Long deviceId = 25L;
  59 + Long tenantId = 1L;
  60 + return appendDeviceMessage(message, deviceId, tenantId, serverId);
  61 + }
  62 +
  63 + /**
  64 + * 补充消息的后端字段
  65 + *
  66 + * @param message 消息
  67 + * @param deviceId 设备编号
  68 + * @param tenantId 租户编号
  69 + * @param serverId 设备连接的 serverId
  70 + * @return 消息
  71 + */
  72 + private IotDeviceMessage appendDeviceMessage(IotDeviceMessage message,
  73 + Long deviceId, Long tenantId, String serverId) {
  74 + message.setId(IotDeviceMessageUtils.generateMessageId()).setReportTime(LocalDateTime.now())
  75 + .setDeviceId(deviceId).setTenantId(tenantId).setServerId(serverId);
  76 + // 特殊:如果设备没有指定 requestId,则使用 messageId
  77 + if (StrUtil.isEmpty(message.getRequestId())) {
  78 + message.setRequestId(message.getId());
  79 + }
  80 + return message;
  81 + }
  82 +
  83 +}