diff --git a/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar b/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar deleted file mode 100644 index fef8d5362c..0000000000 Binary files a/plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar and /dev/null differ diff --git a/plugins/yudao-module-iot-plugin-http-1.0.0.jar b/plugins/yudao-module-iot-plugin-http-1.0.0.jar index a1d4db6c8a..d3c4facae3 100644 Binary files a/plugins/yudao-module-iot-plugin-http-1.0.0.jar and b/plugins/yudao-module-iot-plugin-http-1.0.0.jar differ diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java index 74c5651017..648b7dda65 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/HttpPluginSpringbootApplication.java @@ -1,16 +1,30 @@ package cn.iocoder.yudao.module.iot.plugin.http; +import cn.iocoder.yudao.module.iot.plugin.http.config.VertxService; +import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; +/** + * 独立运行入口 + */ +@Slf4j @SpringBootApplication(scanBasePackages = "cn.iocoder.yudao.module.iot.plugin") public class HttpPluginSpringbootApplication { public static void main(String[] args) { + // 这里可选择 NONE / SERVLET / REACTIVE,看你需求 SpringApplication application = new SpringApplication(HttpPluginSpringbootApplication.class); application.setWebApplicationType(WebApplicationType.NONE); - application.run(args); - } + ConfigurableApplicationContext context = application.run(args); + + // 手动获取 VertxService 并启动 + VertxService vertxService = context.getBean(VertxService.class); + vertxService.startServer(); + + log.info("[HttpPluginSpringbootApplication] 独立模式启动完成"); + } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java index e6145b93d5..85249cdb85 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPlugin.java @@ -2,80 +2,70 @@ package cn.iocoder.yudao.module.iot.plugin.http.config; import cn.hutool.extra.spring.SpringUtil; import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; -import cn.iocoder.yudao.module.iot.plugin.http.service.HttpVertxHandler; -import io.vertx.core.Vertx; -import io.vertx.ext.web.Router; -import io.vertx.ext.web.handler.BodyHandler; import lombok.extern.slf4j.Slf4j; import org.pf4j.PluginWrapper; import org.pf4j.spring.SpringPlugin; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; +/** + * 负责插件的启动和停止,与 Vert.x 的生命周期管理 + */ @Slf4j public class HttpVertxPlugin extends SpringPlugin { - private static final int PORT = 8092; - private Vertx vertx; - public HttpVertxPlugin(PluginWrapper wrapper) { super(wrapper); } @Override public void start() { - log.info("HttpVertxPlugin.start()"); + log.info("[HttpVertxPlugin] start ..."); - // 获取 DeviceDataApi 实例 - DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class); - if (deviceDataApi == null) { - log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!"); + // 1. 获取插件上下文 + ApplicationContext pluginContext = getApplicationContext(); + if (pluginContext == null) { + log.error("[HttpVertxPlugin] pluginContext is null, start failed."); return; } - // 初始化 Vert.x - vertx = Vertx.vertx(); - Router router = Router.router(vertx); - - // 处理 Body - router.route().handler(BodyHandler.create()); - - // 设置路由 - router.post("/sys/:productKey/:deviceName/thing/event/property/post") - .handler(new HttpVertxHandler(deviceDataApi)); - - // 启动 HTTP 服务器 - vertx.createHttpServer() - .requestHandler(router) - .listen(PORT, http -> { - if (http.succeeded()) { - log.info("HTTP 服务器启动成功,端口为: {}", PORT); - } else { - log.error("HTTP 服务器启动失败", http.cause()); - } - }); + // 2. 启动 Vertx + VertxService vertxService = pluginContext.getBean(VertxService.class); + vertxService.startServer(); } + @Override public void stop() { - log.info("HttpVertxPlugin.stop()"); - if (vertx != null) { - vertx.close(ar -> { - if (ar.succeeded()) { - log.info("Vert.x 关闭成功"); - } else { - log.error("Vert.x 关闭失败", ar.cause()); - } - }); + log.info("[HttpVertxPlugin] stop ..."); + ApplicationContext pluginContext = getApplicationContext(); + if (pluginContext != null) { + // 停止服务器 + VertxService vertxService = pluginContext.getBean(VertxService.class); + vertxService.stopServer(); } } @Override protected ApplicationContext createApplicationContext() { - AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); - applicationContext.setClassLoader(getWrapper().getPluginClassLoader()); - applicationContext.refresh(); + AnnotationConfigApplicationContext pluginContext = new AnnotationConfigApplicationContext() { + @Override + protected void prepareRefresh() { + // 在刷新容器前注册主程序中的 Bean + ConfigurableListableBeanFactory beanFactory = this.getBeanFactory(); + DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class); + beanFactory.registerSingleton("deviceDataApi", deviceDataApi); - return applicationContext; + super.prepareRefresh(); + } + }; + + pluginContext.setClassLoader(getWrapper().getPluginClassLoader()); + pluginContext.scan("cn.iocoder.yudao.module.iot.plugin.http.config"); + pluginContext.refresh(); + + return pluginContext; } -} \ No newline at end of file + +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java index b5e977efbb..55bce6f24a 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/HttpVertxPluginConfiguration.java @@ -1,16 +1,67 @@ package cn.iocoder.yudao.module.iot.plugin.http.config; -import org.pf4j.DefaultPluginManager; -import org.pf4j.PluginWrapper; +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; +import cn.iocoder.yudao.module.iot.plugin.http.service.HttpVertxHandler; +import io.vertx.core.Vertx; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.handler.BodyHandler; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +/** + * 插件与独立运行都可复用的配置类 + */ +@Slf4j @Configuration public class HttpVertxPluginConfiguration { - @Bean(initMethod = "start") - public HttpVertxPlugin httpVertxPlugin() { - PluginWrapper pluginWrapper = new PluginWrapper(new DefaultPluginManager(), null, null, null); - return new HttpVertxPlugin(pluginWrapper); + /** + * 可在 application.yml 中配置,默认端口 8092 + */ + @Value("${plugin.http.server.port:8092}") + private Integer port; + + /** + * 创建 Vert.x 实例 + */ + @Bean + public Vertx vertx() { + return Vertx.vertx(); } -} \ No newline at end of file + + /** + * 创建路由 + */ + @Bean + public Router router(Vertx vertx, HttpVertxHandler httpVertxHandler) { + Router router = Router.router(vertx); + + // 处理 Body + router.route().handler(BodyHandler.create()); + + // 设置路由 + router.post("/sys/:productKey/:deviceName/thing/event/property/post") + .handler(httpVertxHandler); + + return router; + } + + /** + * 注入你的 Http 处理器 Handler,依赖 DeviceDataApi + */ + @Bean + public HttpVertxHandler httpVertxHandler(DeviceDataApi deviceDataApi) { + return new HttpVertxHandler(deviceDataApi); + } + + /** + * 定义一个 VertxService 来管理服务器启动逻辑 + * 无论是独立运行还是插件方式,都可以共用此类 + */ + @Bean + public VertxService vertxService(Vertx vertx, Router router) { + return new VertxService(port, vertx, router); + } +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/VertxService.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/VertxService.java new file mode 100644 index 0000000000..5a57be8eee --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/config/VertxService.java @@ -0,0 +1,52 @@ +package cn.iocoder.yudao.module.iot.plugin.http.config; + +import io.vertx.core.Vertx; +import io.vertx.ext.web.Router; +import lombok.extern.slf4j.Slf4j; + +/** + * 封装 Vert.x HTTP 服务的启动/关闭逻辑 + */ +@Slf4j +public class VertxService { + + private final Integer port; + private final Vertx vertx; + private final Router router; + + public VertxService(Integer port, Vertx vertx, Router router) { + this.port = port; + this.vertx = vertx; + this.router = router; + } + + /** + * 启动 HTTP 服务器 + */ + public void startServer() { + vertx.createHttpServer() + .requestHandler(router) + .listen(port, http -> { + if (http.succeeded()) { + log.info("[VertxService] HTTP 服务器启动成功, 端口: {}", port); + } else { + log.error("[VertxService] HTTP 服务器启动失败", http.cause()); + } + }); + } + + /** + * 关闭 HTTP 服务器 + */ + public void stopServer() { + if (vertx != null) { + vertx.close(ar -> { + if (ar.succeeded()) { + log.info("[VertxService] Vert.x 关闭成功"); + } else { + log.error("[VertxService] Vert.x 关闭失败", ar.cause()); + } + }); + } + } +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java index becba2a082..df55c68fd6 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/service/HttpVertxHandler.java @@ -22,77 +22,51 @@ public class HttpVertxHandler implements Handler { public void handle(RoutingContext ctx) { String productKey = ctx.pathParam("productKey"); String deviceName = ctx.pathParam("deviceName"); - RequestBody requestBody = ctx.body(); + RequestBody requestBody = ctx.body(); JSONObject jsonData; try { jsonData = JSONUtil.parseObj(requestBody.asJsonObject()); } catch (Exception e) { - JSONObject res = createResponseJson( - 400, - new JSONObject(), - null, - "请求数据不是合法的 JSON 格式: " + e.getMessage(), - "thing.event.property.post", - "1.0"); - ctx.response() - .setStatusCode(400) + log.error("[HttpVertxHandler] 请求数据解析失败", e); + ctx.response().setStatusCode(400) .putHeader("Content-Type", "application/json; charset=UTF-8") - .end(res.toString()); + .end(createResponseJson(400, null, null, + "请求数据不是合法的 JSON 格式: " + e.getMessage(), + "thing.event.property.post", "1.0").toString()); return; } - String id = jsonData.getStr("id", null); + String id = jsonData.getStr("id"); try { - // 调用主程序的接口保存数据 - IotDevicePropertyReportReqDTO createDTO = IotDevicePropertyReportReqDTO.builder() + IotDevicePropertyReportReqDTO reportReqDTO = IotDevicePropertyReportReqDTO.builder() .productKey(productKey) .deviceName(deviceName) - .params(jsonData) // TODO 芋艿:这块要优化 + .params(jsonData) .build(); - deviceDataApi.reportDevicePropertyData(createDTO); - // 构造成功响应内容 - JSONObject successRes = createResponseJson( - 200, - new JSONObject(), - id, - "success", - "thing.event.property.post", - "1.0"); + deviceDataApi.reportDevicePropertyData(reportReqDTO); + ctx.response() .setStatusCode(200) .putHeader("Content-Type", "application/json; charset=UTF-8") - .end(successRes.toString()); + .end(createResponseJson(200, new JSONObject(), id, "success", + "thing.event.property.post", "1.0").toString()); + } catch (Exception e) { - JSONObject errorRes = createResponseJson( - 500, - new JSONObject(), - id, - "The format of result is error!", - "thing.event.property.post", - "1.0"); + log.error("[HttpVertxHandler] 上报属性数据失败", e); ctx.response() .setStatusCode(500) .putHeader("Content-Type", "application/json; charset=UTF-8") - .end(errorRes.toString()); + .end(createResponseJson(500, new JSONObject(), id, + "The format of result is error!", + "thing.event.property.post", "1.0").toString()); } } - /** - * 创建标准化的响应 JSON 对象 - * - * @param code 响应状态码(业务层面的) - * @param data 返回的数据对象(JSON) - * @param id 请求的 id(可选) - * @param message 返回的提示信息 - * @param method 返回的 method 标识 - * @param version 返回的版本号 - * @return 构造好的 JSON 对象 - */ - private JSONObject createResponseJson(int code, JSONObject data, String id, String message, String method, - String version) { + private JSONObject createResponseJson(int code, JSONObject data, String id, + String message, String method, String version) { JSONObject res = new JSONObject(); res.set("code", code); res.set("data", data != null ? data : new JSONObject());