reactor:【IoT 物联网】将 http component 合并到 gateway 里

This commit is contained in:
YunaiV
2025-06-01 07:48:30 +08:00
parent 81cbc61f3c
commit c3485a3f3d
46 changed files with 524 additions and 922 deletions

View File

@@ -0,0 +1,13 @@
package cn.iocoder.yudao.module.iot.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class IotGatewayServerApplication {
public static void main(String[] args) {
SpringApplication.run(IotGatewayServerApplication.class, args);
}
}

View File

@@ -1 +1,4 @@
/**
* 提供设备接入的各种数据(请求、响应)的编解码
*/
package cn.iocoder.yudao.module.iot.gateway.codec;

View File

@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.iot.gateway.config;
import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpDownstreamSubscriber;
import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(IotGatewayProperties.class)
@Slf4j
public class IotGatewayConfiguration {
/**
* IoT 网关 HTTP 协议配置类
*/
@Configuration
@ConditionalOnProperty(prefix = "yudao.iot.gateway.protocol.http", name = "enabled", havingValue = "true")
@Slf4j
public static class HttpProtocolConfiguration {
@Bean
public IotHttpUpstreamProtocol iotHttpUpstreamProtocol(IotGatewayProperties gatewayProperties,
IotDeviceMessageProducer deviceMessageProducer) {
return new IotHttpUpstreamProtocol(gatewayProperties.getProtocol().getHttp(), deviceMessageProducer);
}
@Bean
public IotHttpDownstreamSubscriber iotHttpDownstreamSubscriber(IotHttpUpstreamProtocol httpUpstreamProtocol,
IotMessageBus messageBus) {
return new IotHttpDownstreamSubscriber(httpUpstreamProtocol,messageBus);
}
}
}

View File

@@ -0,0 +1,109 @@
package cn.iocoder.yudao.module.iot.gateway.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import java.util.List;
@ConfigurationProperties(prefix = "yudao.iot.gateway")
@Validated
@Data
public class IotGatewayProperties {
/**
* 设备 RPC 服务配置
*/
private RpcProperties rpc;
/**
* 协议配置
*/
private ProtocolProperties protocol;
@Data
public static class RpcProperties {
/**
* 主程序 API 地址
*/
private String url;
/**
* 连接超时时间
*/
private String connectTimeout;
/**
* 读取超时时间
*/
private String readTimeout;
}
@Data
public static class ProtocolProperties {
/**
* HTTP 组件配置
*/
private HttpProperties http;
/**
* EMQX 组件配置
*/
private EmqxProperties emqx;
}
@Data
public static class HttpProperties {
/**
* 是否开启
*/
private Boolean enabled;
/**
* 服务端口
*/
private Integer serverPort;
}
@Data
public static class EmqxProperties {
/**
* 是否开启
*/
private Boolean enabled;
/**
* MQTT 服务器地址
*/
private String mqttHost;
/**
* MQTT 服务器端口
*/
private Integer mqttPort;
/**
* MQTT 用户名
*/
private String mqttUsername;
/**
* MQTT 密码
*/
private String mqttPassword;
/**
* MQTT 是否开启 SSL
*/
private Boolean mqttSsl;
/**
* MQTT 主题
*/
private List<String> mqttTopics;
/**
* 认证端口
*/
private Integer authPort;
}
}

View File

@@ -0,0 +1,174 @@
package cn.iocoder.yudao.module.iot.gateway.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* IoT 设备主题枚举
* <p>
* 用于统一管理 MQTT 协议中的主题常量,基于 Alink 协议规范
*
* @author haohao
*/
@RequiredArgsConstructor
@Getter
public enum IotDeviceTopicEnum {
// TODO @haohaoSYS_TOPIC_PREFIX、SERVICE_TOPIC_PREFIX、REPLY_SUFFIX 类似这种,要不搞成这个里面的静态变量?不是枚举值
/**
* 系统主题前缀
*/
SYS_TOPIC_PREFIX("/sys/", "系统主题前缀"),
/**
* 服务调用主题前缀
*/
SERVICE_TOPIC_PREFIX("/thing/service/", "服务调用主题前缀"),
// TODO @haohao注释时中英文之间有个空格
/**
* 设备属性设置主题
* 请求 Topic/sys/${productKey}/${deviceName}/thing/service/property/set
* 响应 Topic/sys/${productKey}/${deviceName}/thing/service/property/set_reply
*/
PROPERTY_SET_TOPIC("/thing/service/property/set", "设备属性设置主题"),
/**
* 设备属性获取主题
* 请求 Topic/sys/${productKey}/${deviceName}/thing/service/property/get
* 响应 Topic/sys/${productKey}/${deviceName}/thing/service/property/get_reply
*/
PROPERTY_GET_TOPIC("/thing/service/property/get", "设备属性获取主题"),
/**
* 设备配置设置主题
* 请求 Topic/sys/${productKey}/${deviceName}/thing/service/config/set
* 响应 Topic/sys/${productKey}/${deviceName}/thing/service/config/set_reply
*/
CONFIG_SET_TOPIC("/thing/service/config/set", "设备配置设置主题"),
/**
* 设备OTA升级主题
* 请求 Topic/sys/${productKey}/${deviceName}/thing/service/ota/upgrade
* 响应 Topic/sys/${productKey}/${deviceName}/thing/service/ota/upgrade_reply
*/
OTA_UPGRADE_TOPIC("/thing/service/ota/upgrade", "设备OTA升级主题"),
/**
* 设备属性上报主题
* 请求 Topic/sys/${productKey}/${deviceName}/thing/event/property/post
* 响应 Topic/sys/${productKey}/${deviceName}/thing/event/property/post_reply
*/
PROPERTY_POST_TOPIC("/thing/event/property/post", "设备属性上报主题"),
/**
* 设备事件上报主题前缀
*/
EVENT_POST_TOPIC_PREFIX("/thing/event/", "设备事件上报主题前缀"),
/**
* 设备事件上报主题后缀
*/
EVENT_POST_TOPIC_SUFFIX("/post", "设备事件上报主题后缀"),
/**
* 响应主题后缀
*/
REPLY_SUFFIX("_reply", "响应主题后缀");
private final String topic;
private final String description;
/**
* 构建设备服务调用主题
*
* @param productKey 产品Key
* @param deviceName 设备名称
* @param serviceIdentifier 服务标识符
* @return 完整的主题路径
*/
public static String buildServiceTopic(String productKey, String deviceName, String serviceIdentifier) {
// TODO @haohao貌似 SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName 是统一的;
return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName +
SERVICE_TOPIC_PREFIX.getTopic() + serviceIdentifier;
}
/**
* 构建设备属性设置主题
*
* @param productKey 产品Key
* @param deviceName 设备名称
* @return 完整的主题路径
*/
public static String buildPropertySetTopic(String productKey, String deviceName) {
return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + PROPERTY_SET_TOPIC.getTopic();
}
/**
* 构建设备属性获取主题
*
* @param productKey 产品Key
* @param deviceName 设备名称
* @return 完整的主题路径
*/
public static String buildPropertyGetTopic(String productKey, String deviceName) {
return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + PROPERTY_GET_TOPIC.getTopic();
}
/**
* 构建设备配置设置主题
*
* @param productKey 产品Key
* @param deviceName 设备名称
* @return 完整的主题路径
*/
public static String buildConfigSetTopic(String productKey, String deviceName) {
return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + CONFIG_SET_TOPIC.getTopic();
}
/**
* 构建设备 OTA 升级主题
*
* @param productKey 产品Key
* @param deviceName 设备名称
* @return 完整的主题路径
*/
public static String buildOtaUpgradeTopic(String productKey, String deviceName) {
return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + OTA_UPGRADE_TOPIC.getTopic();
}
/**
* 构建设备属性上报主题
*
* @param productKey 产品Key
* @param deviceName 设备名称
* @return 完整的主题路径
*/
public static String buildPropertyPostTopic(String productKey, String deviceName) {
return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName + PROPERTY_POST_TOPIC.getTopic();
}
/**
* 构建设备事件上报主题
*
* @param productKey 产品Key
* @param deviceName 设备名称
* @param eventIdentifier 事件标识符
* @return 完整的主题路径
*/
public static String buildEventPostTopic(String productKey, String deviceName, String eventIdentifier) {
return SYS_TOPIC_PREFIX.getTopic() + productKey + "/" + deviceName +
EVENT_POST_TOPIC_PREFIX.getTopic() + eventIdentifier + EVENT_POST_TOPIC_SUFFIX.getTopic();
}
/**
* 获取响应主题
*
* @param requestTopic 请求主题
* @return 响应主题
*/
public static String getReplyTopic(String requestTopic) {
return requestTopic + REPLY_SUFFIX.getTopic();
}
}

View File

@@ -1 +0,0 @@
package cn.iocoder.yudao.module.iot.gateway;

View File

@@ -0,0 +1,44 @@
package cn.iocoder.yudao.module.iot.gateway.protocol.http;
import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageBus;
import cn.iocoder.yudao.module.iot.core.messagebus.core.IotMessageSubscriber;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* IoT 网关 HTTP 订阅者:接收下行给设备的消息
*
* @author 芋道源码
*/
@RequiredArgsConstructor
@Slf4j
public class IotHttpDownstreamSubscriber implements IotMessageSubscriber<IotDeviceMessage> {
private final IotHttpUpstreamProtocol protocol;
private final IotMessageBus messageBus;
@PostConstruct
public void init() {
messageBus.register(this);
}
@Override
public String getTopic() {
return IotDeviceMessage.buildMessageBusGatewayDeviceMessageTopic(protocol.getServerId());
}
@Override
public String getGroup() {
// 保证点对点消费,需要保证独立的 Group所以使用 Topic 作为 Group
return getTopic();
}
@Override
public void onMessage(IotDeviceMessage message) {
log.error("[onMessage][IoT 网关 HTTP 协议不支持下行消息,忽略消息:{}]", message);
}
}

View File

@@ -0,0 +1,76 @@
package cn.iocoder.yudao.module.iot.gateway.protocol.http;
import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
import cn.iocoder.yudao.module.iot.core.util.IotCoreUtils;
import cn.iocoder.yudao.module.iot.gateway.config.IotGatewayProperties;
import cn.iocoder.yudao.module.iot.gateway.protocol.http.router.IotHttpUpstreamHandler;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpServer;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* IoT 网关 HTTP 协议:接收设备上行消息
*
* @author 芋道源码
*/
@RequiredArgsConstructor
@Slf4j
public class IotHttpUpstreamProtocol extends AbstractVerticle {
private final IotGatewayProperties.HttpProperties httpProperties;
private final IotDeviceMessageProducer deviceMessageProducer;
private HttpServer httpServer;
@Override
@PostConstruct
public void start() {
// 创建路由
Vertx vertx = Vertx.vertx();
Router router = Router.router(vertx);
router.route().handler(BodyHandler.create());
// 创建处理器,添加路由处理器
IotHttpUpstreamHandler upstreamHandler = new IotHttpUpstreamHandler(
this, deviceMessageProducer);
router.post(IotHttpUpstreamHandler.PROPERTY_PATH).handler(upstreamHandler);
router.post(IotHttpUpstreamHandler.EVENT_PATH).handler(upstreamHandler);
// 启动 HTTP 服务器
try {
httpServer = vertx.createHttpServer()
.requestHandler(router)
.listen(httpProperties.getServerPort())
.result();
log.info("[start][IoT 网关 HTTP 协议启动成功,端口:{}]", httpProperties.getServerPort());
} catch (Exception e) {
log.error("[start][IoT 网关 HTTP 协议启动失败]", e);
throw e;
}
}
@Override
@PreDestroy
public void stop() {
if (httpServer != null) {
try {
httpServer.close().result();
log.info("[stop][IoT 网关 HTTP 协议已停止]");
} catch (Exception e) {
log.error("[stop][IoT 网关 HTTP 协议停止失败]", e);
}
}
}
public String getServerId() {
return IotCoreUtils.generateServerId(httpProperties.getServerPort());
}
}

View File

@@ -1 +0,0 @@
package cn.iocoder.yudao.module.iot.gateway.protocol.http;

View File

@@ -0,0 +1,322 @@
package cn.iocoder.yudao.module.iot.gateway.protocol.http.router;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.core.mq.producer.IotDeviceMessageProducer;
import cn.iocoder.yudao.module.iot.gateway.enums.IotDeviceTopicEnum;
import cn.iocoder.yudao.module.iot.gateway.protocol.http.IotHttpUpstreamProtocol;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.time.LocalDateTime;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR;
/**
* IoT 网关 HTTP 协议的处理器
*
* @author 芋道源码
*/
@RequiredArgsConstructor
@Slf4j
public class IotHttpUpstreamHandler implements Handler<RoutingContext> {
// TODO @haohao你说咱要不要把 "/sys/:productKey/:deviceName"
// + IotDeviceTopicEnum.PROPERTY_POST_TOPIC.getTopic(),也抽到 IotDeviceTopicEnum 的 build 这种?尽量都收敛掉?
/**
* 属性上报路径
*/
public static final String PROPERTY_PATH = "/sys/:productKey/:deviceName"
+ IotDeviceTopicEnum.PROPERTY_POST_TOPIC.getTopic();
/**
* 事件上报路径
*/
public static final String EVENT_PATH = "/sys/:productKey/:deviceName"
+ IotDeviceTopicEnum.EVENT_POST_TOPIC_PREFIX.getTopic() + ":identifier"
+ IotDeviceTopicEnum.EVENT_POST_TOPIC_SUFFIX.getTopic();
/**
* 事件上报方法前缀
*/
private static final String EVENT_METHOD_PREFIX = "thing.event.";
/**
* 事件上报方法后缀
*/
private static final String EVENT_METHOD_SUFFIX = ".post";
private final IotHttpUpstreamProtocol protocol;
// /**
// * 设备上行 API
// */
// private final IotDeviceUpstreamApi deviceUpstreamApi;
/**
* 设备消息生产者
*/
private final IotDeviceMessageProducer deviceMessageProducer;
@Override
public void handle(RoutingContext routingContext) {
String path = routingContext.request().path();
String requestId = IdUtil.fastSimpleUUID();
try {
// 1. 解析通用参数
Map<String, String> params = parseCommonParams(routingContext, requestId);
String productKey = params.get("productKey");
String deviceName = params.get("deviceName");
JsonObject body = routingContext.body().asJsonObject();
requestId = params.get("requestId");
// 2. 根据路径模式处理不同类型的请求
if (isPropertyPostPath(path)) {
// 处理属性上报
handlePropertyPost(routingContext, productKey, deviceName, requestId, body);
return;
}
if (isEventPostPath(path)) {
// 处理事件上报
String identifier = routingContext.pathParam("identifier");
handleEventPost(routingContext, productKey, deviceName, identifier, requestId, body);
return;
}
// 不支持的请求路径
sendErrorResponse(routingContext, requestId, "unknown", BAD_REQUEST.getCode(), "不支持的请求路径");
} catch (Exception e) {
log.error("[handle][处理上行请求异常] path={}", path, e);
String method = determineMethodFromPath(path, routingContext);
sendErrorResponse(routingContext, requestId, method, INTERNAL_SERVER_ERROR.getCode(),
INTERNAL_SERVER_ERROR.getMsg());
}
}
/**
* 解析通用参数
*
* @param routingContext 路由上下文
* @param defaultRequestId 默认请求 ID
* @return 参数映射
*/
private Map<String, String> parseCommonParams(RoutingContext routingContext, String defaultRequestId) {
Map<String, String> params = MapUtil.newHashMap();
params.put("productKey", routingContext.pathParam("productKey"));
params.put("deviceName", routingContext.pathParam("deviceName"));
JsonObject body = routingContext.body().asJsonObject();
String requestId = ObjUtil.defaultIfNull(body.getString("id"), defaultRequestId);
params.put("requestId", requestId);
return params;
}
/**
* 判断是否是属性上报路径
*
* @param path 路径
* @return 是否是属性上报路径
*/
private boolean isPropertyPostPath(String path) {
return StrUtil.endWith(path, IotDeviceTopicEnum.PROPERTY_POST_TOPIC.getTopic());
}
/**
* 判断是否是事件上报路径
*
* @param path 路径
* @return 是否是事件上报路径
*/
private boolean isEventPostPath(String path) {
return StrUtil.contains(path, IotDeviceTopicEnum.EVENT_POST_TOPIC_PREFIX.getTopic())
&& StrUtil.endWith(path, IotDeviceTopicEnum.EVENT_POST_TOPIC_SUFFIX.getTopic());
}
/**
* 处理属性上报请求
*
* @param routingContext 路由上下文
* @param productKey 产品 Key
* @param deviceName 设备名称
* @param requestId 请求 ID
* @param body 请求体
*/
private void handlePropertyPost(RoutingContext routingContext, String productKey, String deviceName,
String requestId, JsonObject body) {
// 1.1 构建设备消息
String deviceKey = "xxx"; // TODO @芋艿:待支持
Long tenantId = 1L; // TODO @芋艿:待支持
IotDeviceMessage message = IotDeviceMessage.of(productKey, deviceName, deviceKey,
requestId, LocalDateTime.now(),
protocol.getServerId(), tenantId)
.ofPropertyReport(parsePropertiesFromBody(body));
// 1.2 发送消息
deviceMessageProducer.sendDeviceMessage(message);
// 2. 返回响应
sendResponse(routingContext, requestId, null, null);
}
/**
* 处理事件上报请求
*
* @param routingContext 路由上下文
* @param productKey 产品 Key
* @param deviceName 设备名称
* @param identifier 事件标识符
* @param requestId 请求 ID
* @param body 请求体
*/
private void handleEventPost(RoutingContext routingContext, String productKey, String deviceName,
String identifier, String requestId, JsonObject body) {
// // 处理事件上报
// IotDeviceEventReportReqDTO reportReqDTO = parseEventReportRequest(productKey, deviceName, identifier,
// requestId, body);
//
// // 事件上报
// CommonResult<Boolean> result = deviceUpstreamApi.reportDeviceEvent(reportReqDTO);
// String method = EVENT_METHOD_PREFIX + identifier + EVENT_METHOD_SUFFIX;
//
// // 返回响应
// sendResponse(routingContext, requestId, method, result);
}
/**
* 发送响应
*
* @param routingContext 路由上下文
* @param requestId 请求 ID
* @param method 方法名
* @param result 结果
*/
private void sendResponse(RoutingContext routingContext, String requestId, String method,
CommonResult<Boolean> result) {
// // TODO @芋艿:后续再优化
// IotStandardResponse response;
// if (result == null ) {
// response = IotStandardResponse.success(requestId, method, null);
// } else if (result.isSuccess()) {
// response = IotStandardResponse.success(requestId, method, result.getData());
// } else {
// response = IotStandardResponse.error(requestId, method, result.getCode(), result.getMsg());
// }
// IotNetComponentCommonUtils.writeJsonResponse(routingContext, response);
}
/**
* 发送错误响应
*
* @param routingContext 路由上下文
* @param requestId 请求 ID
* @param method 方法名
* @param code 错误代码
* @param message 错误消息
*/
private void sendErrorResponse(RoutingContext routingContext, String requestId, String method, Integer code,
String message) {
// IotStandardResponse errorResponse = IotStandardResponse.error(requestId, method, code, message);
// IotNetComponentCommonUtils.writeJsonResponse(routingContext, errorResponse);
}
/**
* 从路径确定方法名
*
* @param path 路径
* @param routingContext 路由上下文
* @return 方法名
*/
private String determineMethodFromPath(String path, RoutingContext routingContext) {
if (StrUtil.contains(path, "/property/")) {
return null;
}
return EVENT_METHOD_PREFIX
+ (routingContext.pathParams().containsKey("identifier")
? routingContext.pathParam("identifier")
: "unknown")
+
EVENT_METHOD_SUFFIX;
}
// TODO @芋艿:这块在看看
/**
* 从请求体解析属性
*
* @param body 请求体
* @return 属性映射
*/
private Map<String, Object> parsePropertiesFromBody(JsonObject body) {
Map<String, Object> properties = MapUtil.newHashMap();
JsonObject params = body.getJsonObject("params");
if (CollUtil.isEmpty(params)) {
return properties;
}
// 将标准格式的 params 转换为平台需要的 properties 格式
for (String key : params.fieldNames()) {
Object valueObj = params.getValue(key);
// 如果是复杂结构(包含 value 和 time
if (valueObj instanceof JsonObject) {
JsonObject valueJson = (JsonObject) valueObj;
properties.put(key, valueJson.containsKey("value") ? valueJson.getValue("value") : valueObj);
} else {
properties.put(key, valueObj);
}
}
return properties;
}
// /**
// * 解析事件上报请求
// *
// * @param productKey 产品 Key
// * @param deviceName 设备名称
// * @param identifier 事件标识符
// * @param requestId 请求 ID
// * @param body 请求体
// * @return 事件上报请求 DTO
// */
// private IotDeviceEventReportReqDTO parseEventReportRequest(String productKey, String deviceName, String identifier,
// String requestId, JsonObject body) {
// // 解析参数
// Map<String, Object> params = parseParamsFromBody(body);
//
// // 构建事件上报请求 DTO
// return ((IotDeviceEventReportReqDTO) new IotDeviceEventReportReqDTO()
// .setRequestId(requestId)
// .setProcessId(IotNetComponentCommonUtils.getProcessId())
// .setReportTime(LocalDateTime.now())
// .setProductKey(productKey)
// .setDeviceName(deviceName)).setIdentifier(identifier).setParams(params);
// }
/**
* 从请求体解析参数
*
* @param body 请求体
* @return 参数映射
*/
private Map<String, Object> parseParamsFromBody(JsonObject body) {
Map<String, Object> params = MapUtil.newHashMap();
JsonObject paramsJson = body.getJsonObject("params");
if (CollUtil.isEmpty(paramsJson)) {
return params;
}
for (String key : paramsJson.fieldNames()) {
params.put(key, paramsJson.getValue(key));
}
return params;
}
}

View File

@@ -1,4 +1,4 @@
/**
* TODO 占位
* 提供设备接入的各种协议的实现
*/
package cn.iocoder.yudao.module.iot.gateway.protocol;

View File

@@ -0,0 +1,57 @@
spring:
application:
name: iot-gateway-server
--- #################### 消息队列相关 ####################
# rocketmq 配置项,对应 RocketMQProperties 配置类
rocketmq:
name-server: 127.0.0.1:9876 # RocketMQ Namesrv
# Producer 配置项
producer:
group: ${spring.application.name}_PRODUCER # 生产者分组
--- #################### 芋道相关配置 ####################
yudao:
iot:
# 网关配置
gateway:
# 设备 RPC 配置
rpc:
url: http://127.0.0.1:48080 # 主程序 API 地址
connect-timeout: 30s
read-timeout: 30s
# 协议配置
protocol:
# ====================================
# 针对引入的 HTTP 组件的配置
# ====================================
http:
enabled: true
server-port: 8092
# ====================================
# 针对引入的 EMQX 组件的配置
# ====================================
emqx:
enabled: true
mqtt-host: 127.0.0.1
mqtt-port: 1883
mqtt-username: admin
mqtt-password: admin123
mqtt-ssl: false
mqtt-topics:
- "/sys/#"
auth-port: 8101
# 消息总线配置
message-bus:
type: rocketmq # 消息总线的类型
# 日志配置
# TODO 芋艿:是不是可以删除
logging:
level:
cn.iocoder.yudao: INFO
root: INFO