Commit 77b89aad773e021b2a7f9d0c85bdbbec468af000
1 parent
cde6ebf9
【功能完善】IoT: 集成 Vert.x 支持,重构 HTTP 插件为 Vert.x 插件
Showing
11 changed files
with
281 additions
and
214 deletions
yudao-dependencies/pom.xml
| ... | ... | @@ -67,6 +67,7 @@ |
| 67 | 67 | <bizlog-sdk.version>3.0.6</bizlog-sdk.version> |
| 68 | 68 | <mqtt.version>1.2.5</mqtt.version> |
| 69 | 69 | <pf4j-spring.version>0.9.0</pf4j-spring.version> |
| 70 | + <vertx.version>4.4.0</vertx.version> | |
| 70 | 71 | <!-- 三方云服务相关 --> |
| 71 | 72 | <okio.version>3.5.0</okio.version> |
| 72 | 73 | <okhttp3.version>4.11.0</okhttp3.version> |
| ... | ... | @@ -613,6 +614,19 @@ |
| 613 | 614 | <version>${pf4j-spring.version}</version> |
| 614 | 615 | </dependency> |
| 615 | 616 | |
| 617 | + <!-- Vert.x 核心依赖 --> | |
| 618 | + <dependency> | |
| 619 | + <groupId>io.vertx</groupId> | |
| 620 | + <artifactId>vertx-core</artifactId> | |
| 621 | + <version>${vertx.version}</version> | |
| 622 | + </dependency> | |
| 623 | + <!-- Vert.x Web 模块 --> | |
| 624 | + <dependency> | |
| 625 | + <groupId>io.vertx</groupId> | |
| 626 | + <artifactId>vertx-web</artifactId> | |
| 627 | + <version>${vertx.version}</version> | |
| 628 | + </dependency> | |
| 629 | + | |
| 616 | 630 | </dependencies> |
| 617 | 631 | </dependencyManagement> |
| 618 | 632 | ... | ... |
yudao-module-iot/yudao-module-iot-biz/pom.xml
| ... | ... | @@ -64,6 +64,16 @@ |
| 64 | 64 | <artifactId>yudao-spring-boot-starter-excel</artifactId> |
| 65 | 65 | </dependency> |
| 66 | 66 | |
| 67 | + <!-- Vert.x 核心依赖 --> | |
| 68 | + <dependency> | |
| 69 | + <groupId>io.vertx</groupId> | |
| 70 | + <artifactId>vertx-core</artifactId> | |
| 71 | + </dependency> | |
| 72 | + <!-- Vert.x Web 模块 --> | |
| 73 | + <dependency> | |
| 74 | + <groupId>io.vertx</groupId> | |
| 75 | + <artifactId>vertx-web</artifactId> | |
| 76 | + </dependency> | |
| 67 | 77 | <!-- MQTT --> |
| 68 | 78 | <dependency> |
| 69 | 79 | <groupId>org.eclipse.paho</groupId> | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java
| ... | ... | @@ -196,10 +196,8 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { |
| 196 | 196 | .collect(Collectors.toMap(PluginInfoDO::getPluginKey, Function.identity())); |
| 197 | 197 | |
| 198 | 198 | // 3. 获取本机 IP 和 MAC 地址 |
| 199 | - LinkedHashSet<InetAddress> localAddressList = NetUtil.localAddressList(t -> t instanceof Inet4Address); | |
| 200 | - LinkedHashSet<String> ipList = NetUtil.toIpList(localAddressList); | |
| 201 | - String ip = ipList.stream().findFirst().orElse("127.0.0.1"); | |
| 202 | - String mac = NetUtil.getMacAddress(localAddressList.stream().findFirst().orElse(null)); | |
| 199 | + String ip = NetUtil.getLocalhostStr(); | |
| 200 | + String mac = NetUtil.getLocalMacAddress(); | |
| 203 | 201 | String mainId = MAIN_ID + "-" + mac; |
| 204 | 202 | |
| 205 | 203 | // 4. 遍历插件列表,并保存为插件实例 | ... | ... |
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/dependency-reduced-pom.xml
0 → 100644
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | |
| 3 | + <parent> | |
| 4 | + <artifactId>yudao-module-iot-plugin</artifactId> | |
| 5 | + <groupId>cn.iocoder.boot</groupId> | |
| 6 | + <version>2.2.0-snapshot</version> | |
| 7 | + </parent> | |
| 8 | + <modelVersion>4.0.0</modelVersion> | |
| 9 | + <artifactId>yudao-module-iot-http-plugin</artifactId> | |
| 10 | + <name>${project.artifactId}</name> | |
| 11 | + <version>2.2.0-snapshot</version> | |
| 12 | + <description>物联网 插件模块 - http 插件</description> | |
| 13 | + <build> | |
| 14 | + <plugins> | |
| 15 | + <plugin> | |
| 16 | + <artifactId>maven-jar-plugin</artifactId> | |
| 17 | + <version>2.4</version> | |
| 18 | + <configuration> | |
| 19 | + <archive> | |
| 20 | + <manifestEntries> | |
| 21 | + <Plugin-Id>${plugin.id}</Plugin-Id> | |
| 22 | + <Plugin-Class>${plugin.class}</Plugin-Class> | |
| 23 | + <Plugin-Version>${plugin.version}</Plugin-Version> | |
| 24 | + <Plugin-Provider>${plugin.provider}</Plugin-Provider> | |
| 25 | + <Plugin-Description>${plugin.description}</Plugin-Description> | |
| 26 | + <Plugin-Dependencies>${plugin.dependencies}</Plugin-Dependencies> | |
| 27 | + </manifestEntries> | |
| 28 | + </archive> | |
| 29 | + </configuration> | |
| 30 | + </plugin> | |
| 31 | + <plugin> | |
| 32 | + <artifactId>maven-deploy-plugin</artifactId> | |
| 33 | + <configuration> | |
| 34 | + <skip>true</skip> | |
| 35 | + </configuration> | |
| 36 | + </plugin> | |
| 37 | + <plugin> | |
| 38 | + <artifactId>maven-shade-plugin</artifactId> | |
| 39 | + <version>3.4.1</version> | |
| 40 | + <executions> | |
| 41 | + <execution> | |
| 42 | + <phase>package</phase> | |
| 43 | + <goals> | |
| 44 | + <goal>shade</goal> | |
| 45 | + </goals> | |
| 46 | + <configuration> | |
| 47 | + <shadedArtifactAttached>true</shadedArtifactAttached> | |
| 48 | + <shadedClassifierName>shaded</shadedClassifierName> | |
| 49 | + <transformers> | |
| 50 | + <transformer> | |
| 51 | + <mainClass>cn.iocoder.yudao.module.iot.HttpPluginSpringbootApplication</mainClass> | |
| 52 | + </transformer> | |
| 53 | + </transformers> | |
| 54 | + </configuration> | |
| 55 | + </execution> | |
| 56 | + </executions> | |
| 57 | + </plugin> | |
| 58 | + </plugins> | |
| 59 | + </build> | |
| 60 | + <dependencies> | |
| 61 | + <dependency> | |
| 62 | + <groupId>org.pf4j</groupId> | |
| 63 | + <artifactId>pf4j-spring</artifactId> | |
| 64 | + <version>0.9.0</version> | |
| 65 | + <scope>provided</scope> | |
| 66 | + </dependency> | |
| 67 | + <dependency> | |
| 68 | + <groupId>org.projectlombok</groupId> | |
| 69 | + <artifactId>lombok</artifactId> | |
| 70 | + <version>1.18.34</version> | |
| 71 | + <scope>provided</scope> | |
| 72 | + </dependency> | |
| 73 | + </dependencies> | |
| 74 | + <properties> | |
| 75 | + <plugin.class>cn.iocoder.yudao.module.iot.plugin.HttpVertxPlugin</plugin.class> | |
| 76 | + <plugin.version>0.0.1</plugin.version> | |
| 77 | + <plugin.id>http-plugin</plugin.id> | |
| 78 | + <plugin.description>http-plugin-0.0.1</plugin.description> | |
| 79 | + <plugin.provider>ahh</plugin.provider> | |
| 80 | + </properties> | |
| 81 | +</project> | ... | ... |
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/plugin.properties
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/pom.xml
| ... | ... | @@ -21,7 +21,7 @@ |
| 21 | 21 | <properties> |
| 22 | 22 | <!-- 插件相关 --> |
| 23 | 23 | <plugin.id>http-plugin</plugin.id> |
| 24 | - <plugin.class>cn.iocoder.yudao.module.iot.plugin.HttpPlugin</plugin.class> | |
| 24 | + <plugin.class>cn.iocoder.yudao.module.iot.plugin.HttpVertxPlugin</plugin.class> | |
| 25 | 25 | <plugin.version>0.0.1</plugin.version> |
| 26 | 26 | <plugin.provider>ahh</plugin.provider> |
| 27 | 27 | <plugin.description>http-plugin-0.0.1</plugin.description> |
| ... | ... | @@ -30,27 +30,6 @@ |
| 30 | 30 | |
| 31 | 31 | <build> |
| 32 | 32 | <plugins> |
| 33 | - <!-- DOESN'T WORK WITH MAVEN 3 (I defined the plugin metadata in properties section) | |
| 34 | - <plugin> | |
| 35 | - <groupId>org.codehaus.mojo</groupId> | |
| 36 | - <artifactId>properties-maven-plugin</artifactId> | |
| 37 | - <version>1.0-alpha-2</version> | |
| 38 | - <executions> | |
| 39 | - <execution> | |
| 40 | - <phase>initialize</phase> | |
| 41 | - <goals> | |
| 42 | - <goal>read-project-properties</goal> | |
| 43 | - </goals> | |
| 44 | - <configuration> | |
| 45 | - <files> | |
| 46 | - <file>plugin.properties</file> | |
| 47 | - </files> | |
| 48 | - </configuration> | |
| 49 | - </execution> | |
| 50 | - </executions> | |
| 51 | - </plugin> | |
| 52 | - --> | |
| 53 | - | |
| 54 | 33 | <plugin> |
| 55 | 34 | <groupId>org.apache.maven.plugins</groupId> |
| 56 | 35 | <artifactId>maven-antrun-plugin</artifactId> |
| ... | ... | @@ -118,6 +97,29 @@ |
| 118 | 97 | <skip>true</skip> |
| 119 | 98 | </configuration> |
| 120 | 99 | </plugin> |
| 100 | + | |
| 101 | + <!-- <plugin>--> | |
| 102 | + <!-- <groupId>org.apache.maven.plugins</groupId>--> | |
| 103 | + <!-- <artifactId>maven-shade-plugin</artifactId>--> | |
| 104 | + <!-- <version>3.4.1</version>--> | |
| 105 | + <!-- <executions>--> | |
| 106 | + <!-- <execution>--> | |
| 107 | + <!-- <phase>package</phase>--> | |
| 108 | + <!-- <goals>--> | |
| 109 | + <!-- <goal>shade</goal>--> | |
| 110 | + <!-- </goals>--> | |
| 111 | + <!-- <configuration>--> | |
| 112 | + <!-- <shadedArtifactAttached>true</shadedArtifactAttached>--> | |
| 113 | + <!-- <shadedClassifierName>shaded</shadedClassifierName>--> | |
| 114 | + <!-- <transformers>--> | |
| 115 | + <!-- <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">--> | |
| 116 | + <!-- <mainClass>cn.iocoder.yudao.module.iot.HttpPluginSpringbootApplication</mainClass>--> | |
| 117 | + <!-- </transformer>--> | |
| 118 | + <!-- </transformers>--> | |
| 119 | + <!-- </configuration>--> | |
| 120 | + <!-- </execution>--> | |
| 121 | + <!-- </executions>--> | |
| 122 | + <!-- </plugin>--> | |
| 121 | 123 | </plugins> |
| 122 | 124 | </build> |
| 123 | 125 | |
| ... | ... | @@ -145,10 +147,15 @@ |
| 145 | 147 | <version>${lombok.version}</version> |
| 146 | 148 | <scope>provided</scope> |
| 147 | 149 | </dependency> |
| 150 | + <!-- Vert.x 核心依赖 --> | |
| 151 | + <dependency> | |
| 152 | + <groupId>io.vertx</groupId> | |
| 153 | + <artifactId>vertx-core</artifactId> | |
| 154 | + </dependency> | |
| 155 | + <!-- Vert.x Web 模块 --> | |
| 148 | 156 | <dependency> |
| 149 | - <groupId>io.netty</groupId> | |
| 150 | - <artifactId>netty-all</artifactId> | |
| 151 | - <version>4.1.63.Final</version> <!-- 版本可根据需要调整 --> | |
| 157 | + <groupId>io.vertx</groupId> | |
| 158 | + <artifactId>vertx-web</artifactId> | |
| 152 | 159 | </dependency> |
| 153 | 160 | <!-- MQTT --> |
| 154 | 161 | <dependency> | ... | ... |
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpPlugin.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.plugin; | |
| 2 | - | |
| 3 | -import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; | |
| 4 | -import cn.iocoder.yudao.module.iot.api.ServiceRegistry; | |
| 5 | -import io.netty.bootstrap.ServerBootstrap; | |
| 6 | -import io.netty.channel.*; | |
| 7 | -import io.netty.channel.nio.NioEventLoopGroup; | |
| 8 | -import io.netty.channel.socket.nio.NioServerSocketChannel; | |
| 9 | -import io.netty.handler.codec.http.*; | |
| 10 | -import lombok.extern.slf4j.Slf4j; | |
| 11 | -import org.pf4j.PluginWrapper; | |
| 12 | -import org.pf4j.Plugin; | |
| 13 | - | |
| 14 | -import java.util.concurrent.ExecutorService; | |
| 15 | -import java.util.concurrent.Executors; | |
| 16 | - | |
| 17 | -@Slf4j | |
| 18 | -public class HttpPlugin extends Plugin { | |
| 19 | - | |
| 20 | - private static final int PORT = 8092; | |
| 21 | - | |
| 22 | - private ExecutorService executorService; | |
| 23 | - private DeviceDataApi deviceDataApi; | |
| 24 | - | |
| 25 | - public HttpPlugin(PluginWrapper wrapper) { | |
| 26 | - super(wrapper); | |
| 27 | - // 初始化线程池 | |
| 28 | - this.executorService = Executors.newSingleThreadExecutor(); | |
| 29 | - } | |
| 30 | - | |
| 31 | - @Override | |
| 32 | - public void start() { | |
| 33 | - log.info("HttpPlugin.start()"); | |
| 34 | - | |
| 35 | - // 重新初始化线程池,确保它是活跃的 | |
| 36 | - if (executorService.isShutdown() || executorService.isTerminated()) { | |
| 37 | - executorService = Executors.newSingleThreadExecutor(); | |
| 38 | - } | |
| 39 | - | |
| 40 | - // 从 ServiceRegistry 中获取主程序暴露的 DeviceDataApi 接口实例 | |
| 41 | - deviceDataApi = ServiceRegistry.getService(DeviceDataApi.class); | |
| 42 | - if (deviceDataApi == null) { | |
| 43 | - log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!"); | |
| 44 | - return; | |
| 45 | - } | |
| 46 | - | |
| 47 | - // 异步启动 Netty 服务器 | |
| 48 | - executorService.submit(this::startHttpServer); | |
| 49 | - } | |
| 50 | - | |
| 51 | - @Override | |
| 52 | - public void stop() { | |
| 53 | - log.info("HttpPlugin.stop()"); | |
| 54 | - // 停止线程池 | |
| 55 | - executorService.shutdownNow(); | |
| 56 | - } | |
| 57 | - | |
| 58 | - /** | |
| 59 | - * 启动 HTTP 服务 | |
| 60 | - */ | |
| 61 | - private void startHttpServer() { | |
| 62 | - EventLoopGroup bossGroup = new NioEventLoopGroup(1); | |
| 63 | - EventLoopGroup workerGroup = new NioEventLoopGroup(); | |
| 64 | - | |
| 65 | - try { | |
| 66 | - ServerBootstrap bootstrap = new ServerBootstrap(); | |
| 67 | - bootstrap.group(bossGroup, workerGroup) | |
| 68 | - .channel(NioServerSocketChannel.class) | |
| 69 | - .childHandler(new ChannelInitializer<>() { | |
| 70 | - | |
| 71 | - @Override | |
| 72 | - protected void initChannel(Channel channel) { | |
| 73 | - channel.pipeline().addLast(new HttpServerCodec()); | |
| 74 | - channel.pipeline().addLast(new HttpObjectAggregator(65536)); | |
| 75 | - // 将从 ServiceRegistry 获取的 deviceDataApi 传入处理器 | |
| 76 | - channel.pipeline().addLast(new HttpHandler(deviceDataApi)); | |
| 77 | - } | |
| 78 | - | |
| 79 | - }); | |
| 80 | - | |
| 81 | - // 绑定端口并启动服务器 | |
| 82 | - ChannelFuture future = bootstrap.bind(PORT).sync(); | |
| 83 | - log.info("HTTP 服务器启动成功,端口为: {}", PORT); | |
| 84 | - future.channel().closeFuture().sync(); | |
| 85 | - } catch (InterruptedException e) { | |
| 86 | - Thread.currentThread().interrupt(); | |
| 87 | - log.warn("HTTP 服务启动被中断", e); | |
| 88 | - } finally { | |
| 89 | - bossGroup.shutdownGracefully(); | |
| 90 | - workerGroup.shutdownGracefully(); | |
| 91 | - } | |
| 92 | - } | |
| 93 | - | |
| 94 | -} | |
| 95 | 0 | \ No newline at end of file |
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpHandler.java renamed to yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxHandler.java
| ... | ... | @@ -4,63 +4,29 @@ import cn.hutool.json.JSONObject; |
| 4 | 4 | import cn.hutool.json.JSONUtil; |
| 5 | 5 | import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; |
| 6 | 6 | import cn.iocoder.yudao.module.iot.api.device.dto.DeviceDataCreateReqDTO; |
| 7 | -import io.netty.buffer.Unpooled; | |
| 8 | -import io.netty.channel.ChannelFutureListener; | |
| 9 | -import io.netty.channel.ChannelHandlerContext; | |
| 10 | -import io.netty.channel.SimpleChannelInboundHandler; | |
| 11 | -import io.netty.handler.codec.http.*; | |
| 12 | -import io.netty.util.CharsetUtil; | |
| 7 | +import io.vertx.core.Handler; | |
| 8 | +import io.vertx.ext.web.RequestBody; | |
| 9 | +import io.vertx.ext.web.RoutingContext; | |
| 10 | +import lombok.extern.slf4j.Slf4j; | |
| 13 | 11 | |
| 14 | -/** | |
| 15 | - * 基于 Netty 的 HTTP 处理器,用于接收设备上报的数据并调用主程序的 DeviceDataApi 接口进行处理。 | |
| 16 | - * <p> | |
| 17 | - * 1. 请求格式:JSON 格式,地址为 POST /sys/{productKey}/{deviceName}/thing/event/property/post | |
| 18 | - * 2. 返回结果:JSON 格式,包含统一的 code、data、id、message、method、version 字段 | |
| 19 | - */ | |
| 20 | -public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { | |
| 12 | +@Slf4j | |
| 13 | +public class HttpVertxHandler implements Handler<RoutingContext> { | |
| 21 | 14 | |
| 22 | 15 | private final DeviceDataApi deviceDataApi; |
| 23 | 16 | |
| 24 | - public HttpHandler(DeviceDataApi deviceDataApi) { | |
| 17 | + public HttpVertxHandler(DeviceDataApi deviceDataApi) { | |
| 25 | 18 | this.deviceDataApi = deviceDataApi; |
| 26 | 19 | } |
| 27 | 20 | |
| 28 | 21 | @Override |
| 29 | - protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) { | |
| 30 | - // 期望的路径格式: /sys/{productKey}/{deviceName}/thing/event/property/post | |
| 31 | - // 使用 "/" 拆分路径 | |
| 32 | - String uri = request.uri(); | |
| 33 | - String[] parts = uri.split("/"); | |
| 22 | + public void handle(RoutingContext ctx) { | |
| 23 | + String productKey = ctx.pathParam("productKey"); | |
| 24 | + String deviceName = ctx.pathParam("deviceName"); | |
| 25 | + RequestBody requestBody = ctx.body(); | |
| 34 | 26 | |
| 35 | - /* | |
| 36 | - 拆分结果示例: | |
| 37 | - parts[0] = "" | |
| 38 | - parts[1] = "sys" | |
| 39 | - parts[2] = productKey | |
| 40 | - parts[3] = deviceName | |
| 41 | - parts[4] = "thing" | |
| 42 | - parts[5] = "event" | |
| 43 | - parts[6] = "property" | |
| 44 | - parts[7] = "post" | |
| 45 | - */ | |
| 46 | - boolean isCorrectPath = parts.length == 8 | |
| 47 | - && "sys".equals(parts[1]) | |
| 48 | - && "thing".equals(parts[4]) | |
| 49 | - && "event".equals(parts[5]) | |
| 50 | - && "property".equals(parts[6]) | |
| 51 | - && "post".equals(parts[7]); | |
| 52 | - if (!isCorrectPath) { | |
| 53 | - writeResponse(ctx, HttpResponseStatus.NOT_FOUND, "Not Found"); | |
| 54 | - return; | |
| 55 | - } | |
| 56 | - String productKey = parts[2]; | |
| 57 | - String deviceName = parts[3]; | |
| 58 | - | |
| 59 | - // 从请求中获取原始数据,尝试解析请求数据为 JSON 对象 | |
| 60 | - String requestBody = request.content().toString(CharsetUtil.UTF_8); | |
| 61 | 27 | JSONObject jsonData; |
| 62 | 28 | try { |
| 63 | - jsonData = JSONUtil.parseObj(requestBody); | |
| 29 | + jsonData = JSONUtil.parseObj(requestBody.asJsonObject()); | |
| 64 | 30 | } catch (Exception e) { |
| 65 | 31 | JSONObject res = createResponseJson( |
| 66 | 32 | 400, |
| ... | ... | @@ -68,11 +34,14 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { |
| 68 | 34 | null, |
| 69 | 35 | "请求数据不是合法的 JSON 格式: " + e.getMessage(), |
| 70 | 36 | "thing.event.property.post", |
| 71 | - "1.0" | |
| 72 | - ); | |
| 73 | - writeResponse(ctx, HttpResponseStatus.BAD_REQUEST, res.toString()); | |
| 37 | + "1.0"); | |
| 38 | + ctx.response() | |
| 39 | + .setStatusCode(400) | |
| 40 | + .putHeader("Content-Type", "application/json; charset=UTF-8") | |
| 41 | + .end(res.toString()); | |
| 74 | 42 | return; |
| 75 | 43 | } |
| 44 | + | |
| 76 | 45 | String id = jsonData.getStr("id", null); |
| 77 | 46 | |
| 78 | 47 | try { |
| ... | ... | @@ -91,9 +60,11 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { |
| 91 | 60 | id, |
| 92 | 61 | "success", |
| 93 | 62 | "thing.event.property.post", |
| 94 | - "1.0" | |
| 95 | - ); | |
| 96 | - writeResponse(ctx, HttpResponseStatus.OK, successRes.toString()); | |
| 63 | + "1.0"); | |
| 64 | + ctx.response() | |
| 65 | + .setStatusCode(200) | |
| 66 | + .putHeader("Content-Type", "application/json; charset=UTF-8") | |
| 67 | + .end(successRes.toString()); | |
| 97 | 68 | } catch (Exception e) { |
| 98 | 69 | JSONObject errorRes = createResponseJson( |
| 99 | 70 | 500, |
| ... | ... | @@ -101,9 +72,11 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { |
| 101 | 72 | id, |
| 102 | 73 | "The format of result is error!", |
| 103 | 74 | "thing.event.property.post", |
| 104 | - "1.0" | |
| 105 | - ); | |
| 106 | - writeResponse(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR, errorRes.toString()); | |
| 75 | + "1.0"); | |
| 76 | + ctx.response() | |
| 77 | + .setStatusCode(500) | |
| 78 | + .putHeader("Content-Type", "application/json; charset=UTF-8") | |
| 79 | + .end(errorRes.toString()); | |
| 107 | 80 | } |
| 108 | 81 | } |
| 109 | 82 | |
| ... | ... | @@ -118,7 +91,8 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { |
| 118 | 91 | * @param version 返回的版本号 |
| 119 | 92 | * @return 构造好的 JSON 对象 |
| 120 | 93 | */ |
| 121 | - private JSONObject createResponseJson(int code, JSONObject data, String id, String message, String method, String version) { | |
| 94 | + private JSONObject createResponseJson(int code, JSONObject data, String id, String message, String method, | |
| 95 | + String version) { | |
| 122 | 96 | JSONObject res = new JSONObject(); |
| 123 | 97 | res.set("code", code); |
| 124 | 98 | res.set("data", data != null ? data : new JSONObject()); |
| ... | ... | @@ -128,26 +102,4 @@ public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> { |
| 128 | 102 | res.set("version", version); |
| 129 | 103 | return res; |
| 130 | 104 | } |
| 131 | - | |
| 132 | - /** | |
| 133 | - * 向客户端返回 HTTP 响应的辅助方法 | |
| 134 | - * | |
| 135 | - * @param ctx 通道上下文 | |
| 136 | - * @param status HTTP 响应状态码(网络层面的) | |
| 137 | - * @param content 响应内容(JSON 字符串或其他文本) | |
| 138 | - */ | |
| 139 | - private void writeResponse(ChannelHandlerContext ctx, HttpResponseStatus status, String content) { | |
| 140 | - // 设置响应头为 JSON 类型和正确的编码 | |
| 141 | - FullHttpResponse response = new DefaultFullHttpResponse( | |
| 142 | - HttpVersion.HTTP_1_1, | |
| 143 | - status, | |
| 144 | - Unpooled.copiedBuffer(content, CharsetUtil.UTF_8) | |
| 145 | - ); | |
| 146 | - response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json; charset=UTF-8"); | |
| 147 | - response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes()); | |
| 148 | - | |
| 149 | - // 发送响应并在发送完成后关闭连接 | |
| 150 | - ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); | |
| 151 | - } | |
| 152 | - | |
| 153 | -} | |
| 154 | 105 | \ No newline at end of file |
| 106 | +} | ... | ... |
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxPlugin.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.plugin; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.module.iot.api.ServiceRegistry; | |
| 4 | +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; | |
| 5 | +import io.vertx.core.Vertx; | |
| 6 | +import io.vertx.ext.web.Router; | |
| 7 | +import io.vertx.ext.web.handler.BodyHandler; | |
| 8 | +import org.pf4j.Plugin; | |
| 9 | +import org.pf4j.PluginWrapper; | |
| 10 | +import lombok.extern.slf4j.Slf4j; | |
| 11 | + | |
| 12 | +@Slf4j | |
| 13 | +public class HttpVertxPlugin extends Plugin { | |
| 14 | + | |
| 15 | + private static final int PORT = 8092; | |
| 16 | + private Vertx vertx; | |
| 17 | + private DeviceDataApi deviceDataApi; | |
| 18 | + | |
| 19 | + public HttpVertxPlugin(PluginWrapper wrapper) { | |
| 20 | + super(wrapper); | |
| 21 | + } | |
| 22 | + | |
| 23 | + @Override | |
| 24 | + public void start() { | |
| 25 | + log.info("HttpVertxPlugin.start()"); | |
| 26 | + | |
| 27 | + // 获取 DeviceDataApi 实例 | |
| 28 | + deviceDataApi = ServiceRegistry.getService(DeviceDataApi.class); | |
| 29 | + if (deviceDataApi == null) { | |
| 30 | + log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!"); | |
| 31 | + return; | |
| 32 | + } | |
| 33 | + | |
| 34 | + // 初始化 Vert.x | |
| 35 | + vertx = Vertx.vertx(); | |
| 36 | + Router router = Router.router(vertx); | |
| 37 | + | |
| 38 | + // 处理 Body | |
| 39 | + router.route().handler(BodyHandler.create()); | |
| 40 | + | |
| 41 | + // 设置路由 | |
| 42 | + router.post("/sys/:productKey/:deviceName/thing/event/property/post") | |
| 43 | + .handler(new HttpVertxHandler(deviceDataApi)); | |
| 44 | + | |
| 45 | + // 启动 HTTP 服务器 | |
| 46 | + vertx.createHttpServer() | |
| 47 | + .requestHandler(router) | |
| 48 | + .listen(PORT, http -> { | |
| 49 | + if (http.succeeded()) { | |
| 50 | + log.info("HTTP 服务器启动成功,端口为: {}", PORT); | |
| 51 | + } else { | |
| 52 | + log.error("HTTP 服务器启动失败", http.cause()); | |
| 53 | + } | |
| 54 | + }); | |
| 55 | + } | |
| 56 | + | |
| 57 | + @Override | |
| 58 | + public void stop() { | |
| 59 | + log.info("HttpVertxPlugin.stop()"); | |
| 60 | + if (vertx != null) { | |
| 61 | + vertx.close(ar -> { | |
| 62 | + if (ar.succeeded()) { | |
| 63 | + log.info("Vert.x 关闭成功"); | |
| 64 | + } else { | |
| 65 | + log.error("Vert.x 关闭失败", ar.cause()); | |
| 66 | + } | |
| 67 | + }); | |
| 68 | + } | |
| 69 | + } | |
| 70 | +} | |
| 0 | 71 | \ No newline at end of file | ... | ... |
yudao-server/src/main/resources/application-dev.yaml
| ... | ... | @@ -212,4 +212,9 @@ iot: |
| 212 | 212 | # 保持连接 |
| 213 | 213 | keepalive: 60 |
| 214 | 214 | # 清除会话(设置为false,断开连接,重连后使用原来的会话 保留订阅的主题,能接收离线期间的消息) |
| 215 | - clearSession: true | |
| 216 | 215 | \ No newline at end of file |
| 216 | + clearSession: true | |
| 217 | + | |
| 218 | + | |
| 219 | +# 插件配置 | |
| 220 | +pf4j: | |
| 221 | + pluginsDir: ${user.home}/plugins # 插件目录 | |
| 217 | 222 | \ No newline at end of file | ... | ... |
yudao-server/src/main/resources/application-local.yaml
| ... | ... | @@ -45,7 +45,7 @@ spring: |
| 45 | 45 | primary: master |
| 46 | 46 | datasource: |
| 47 | 47 | master: |
| 48 | - url: jdbc:mysql://127.0.0.1:3307/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 | |
| 48 | + url: jdbc:mysql://chaojiniu.top:23306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 | |
| 49 | 49 | # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai # MySQL Connector/J 5.X 连接的示例 |
| 50 | 50 | # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 |
| 51 | 51 | # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 |
| ... | ... | @@ -53,8 +53,8 @@ spring: |
| 53 | 53 | # url: jdbc:dm://127.0.0.1:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 |
| 54 | 54 | # url: jdbc:kingbase8://127.0.0.1:54321/test # 人大金仓 KingbaseES 连接的示例 |
| 55 | 55 | # url: jdbc:postgresql://127.0.0.1:5432/postgres # OpenGauss 连接的示例 |
| 56 | - username: root | |
| 57 | - password: ahh@123456 | |
| 56 | + username: ruoyi-vue-pro | |
| 57 | + password: ruoyi-@h2ju02hebp | |
| 58 | 58 | # username: sa # SQL Server 连接的示例 |
| 59 | 59 | # password: Yudao@2024 # SQL Server 连接的示例 |
| 60 | 60 | # username: SYSDBA # DM 连接的示例 |
| ... | ... | @@ -63,17 +63,25 @@ spring: |
| 63 | 63 | # password: Yudao@2024 # OpenGauss 连接的示例 |
| 64 | 64 | slave: # 模拟从库,可根据自己需要修改 |
| 65 | 65 | lazy: true # 开启懒加载,保证启动速度 |
| 66 | - url: jdbc:mysql://127.0.0.1:3307/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true | |
| 66 | + url: jdbc:mysql://chaojiniu.top:23306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true | |
| 67 | + username: ruoyi-vue-pro | |
| 68 | + password: ruoyi-@h2ju02hebp | |
| 69 | + tdengine: # IOT 数据库 | |
| 70 | +# lazy: true # 开启懒加载,保证启动速度 | |
| 71 | + url: jdbc:TAOS-RS://chaojiniu.top:6041/ruoyi_vue_pro | |
| 72 | + driver-class-name: com.taosdata.jdbc.rs.RestfulDriver | |
| 67 | 73 | username: root |
| 68 | - password: ahh@123456 | |
| 74 | + password: taosdata | |
| 75 | + druid: | |
| 76 | + validation-query: SELECT SERVER_STATUS() # TDengine 数据源的有效性检查 SQL | |
| 69 | 77 | |
| 70 | 78 | # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 |
| 71 | 79 | data: |
| 72 | 80 | redis: |
| 73 | - host: 127.0.0.1 # 地址 | |
| 81 | + host: chaojiniu.top # 地址 | |
| 74 | 82 | port: 6379 # 端口 |
| 75 | - database: 0 # 数据库索引 | |
| 76 | -# password: dev # 密码,建议生产环境开启 | |
| 83 | + database: 15 # 数据库索引 | |
| 84 | + password: fsknKD7UvQYZsyf2hXXn # 密码,建议生产环境开启 | |
| 77 | 85 | |
| 78 | 86 | --- #################### 定时任务相关配置 #################### |
| 79 | 87 | |
| ... | ... | @@ -175,8 +183,10 @@ logging: |
| 175 | 183 | cn.iocoder.yudao.module.crm.dal.mysql: debug |
| 176 | 184 | cn.iocoder.yudao.module.erp.dal.mysql: debug |
| 177 | 185 | cn.iocoder.yudao.module.iot.dal.mysql: debug |
| 186 | + cn.iocoder.yudao.module.iot.dal.tdengine: DEBUG | |
| 178 | 187 | cn.iocoder.yudao.module.ai.dal.mysql: debug |
| 179 | 188 | org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 |
| 189 | + com.taosdata: DEBUG # TDengine 的日志级别 | |
| 180 | 190 | |
| 181 | 191 | debug: false |
| 182 | 192 | |
| ... | ... | @@ -259,7 +269,7 @@ justauth: |
| 259 | 269 | iot: |
| 260 | 270 | emq: |
| 261 | 271 | # 账号 |
| 262 | - username: anhaohao | |
| 272 | + username: haohao | |
| 263 | 273 | # 密码 |
| 264 | 274 | password: ahh@123456 |
| 265 | 275 | # 主机地址 |
| ... | ... | @@ -271,4 +281,18 @@ iot: |
| 271 | 281 | # 保持连接 |
| 272 | 282 | keepalive: 60 |
| 273 | 283 | # 清除会话(设置为false,断开连接,重连后使用原来的会话 保留订阅的主题,能接收离线期间的消息) |
| 274 | - clearSession: true | |
| 275 | 284 | \ No newline at end of file |
| 285 | + clearSession: true | |
| 286 | + | |
| 287 | +# MQTT-RPC 配置 | |
| 288 | +mqtt: | |
| 289 | + broker: tcp://chaojiniu.top:1883 | |
| 290 | + username: haohao | |
| 291 | + password: ahh@123456 | |
| 292 | + clientId: mqtt-rpc-server-${random.int} | |
| 293 | + requestTopic: rpc/request | |
| 294 | + responseTopicPrefix: rpc/response/ | |
| 295 | + | |
| 296 | + | |
| 297 | +# 插件配置 | |
| 298 | +pf4j: | |
| 299 | + pluginsDir: /Users/anhaohao/code/gitee/ruoyi-vue-pro/plugins # 插件目录 | |
| 276 | 300 | \ No newline at end of file | ... | ... |