Commit 120029bb1793737d6e0c8d429d7c2d43eb925fe6
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
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
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<RoutingContext> | @@ -33,7 +33,7 @@ public abstract class IotHttpAbstractHandler implements Handler<RoutingContext> | ||
| 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<RoutingContext> { | @@ -101,7 +94,8 @@ public class IotHttpUpstreamHandler implements Handler<RoutingContext> { | ||
| 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 | +} |