Commit 88ef8ba2e3be7eed2add375dbc07eaf029df8742

Authored by 安浩浩
1 parent 698cec92

【功能完善】IoT: 删除旧版 HTTP 插件,重构 HttpPlugin 以支持独立启动,新增 VertxService 管理 HTTP 服务器逻辑,优化代码…

…结构和异常处理,更新相关配置以提升插件性能和可维护性。
plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar deleted
No preview for this file type
plugins/yudao-module-iot-plugin-http-1.0.0.jar
No preview for this file type
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 1 package cn.iocoder.yudao.module.iot.plugin.http;
2 2  
  3 +import cn.iocoder.yudao.module.iot.plugin.http.config.VertxService;
  4 +import lombok.extern.slf4j.Slf4j;
3 5 import org.springframework.boot.SpringApplication;
4 6 import org.springframework.boot.WebApplicationType;
5 7 import org.springframework.boot.autoconfigure.SpringBootApplication;
  8 +import org.springframework.context.ConfigurableApplicationContext;
6 9  
  10 +/**
  11 + * 独立运行入口
  12 + */
  13 +@Slf4j
7 14 @SpringBootApplication(scanBasePackages = "cn.iocoder.yudao.module.iot.plugin")
8 15 public class HttpPluginSpringbootApplication {
9 16  
10 17 public static void main(String[] args) {
  18 + // 这里可选择 NONE / SERVLET / REACTIVE,看你需求
11 19 SpringApplication application = new SpringApplication(HttpPluginSpringbootApplication.class);
12 20 application.setWebApplicationType(WebApplicationType.NONE);
13   - application.run(args);
14   - }
15 21  
  22 + ConfigurableApplicationContext context = application.run(args);
  23 +
  24 + // 手动获取 VertxService 并启动
  25 + VertxService vertxService = context.getBean(VertxService.class);
  26 + vertxService.startServer();
  27 +
  28 + log.info("[HttpPluginSpringbootApplication] 独立模式启动完成");
  29 + }
16 30 }
17 31 \ No newline at end of file
... ...
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;
2 2  
3 3 import cn.hutool.extra.spring.SpringUtil;
4 4 import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
5   -import cn.iocoder.yudao.module.iot.plugin.http.service.HttpVertxHandler;
6   -import io.vertx.core.Vertx;
7   -import io.vertx.ext.web.Router;
8   -import io.vertx.ext.web.handler.BodyHandler;
9 5 import lombok.extern.slf4j.Slf4j;
10 6 import org.pf4j.PluginWrapper;
11 7 import org.pf4j.spring.SpringPlugin;
  8 +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
12 9 import org.springframework.context.ApplicationContext;
13 10 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
14 11  
  12 +/**
  13 + * 负责插件的启动和停止,与 Vert.x 的生命周期管理
  14 + */
15 15 @Slf4j
16 16 public class HttpVertxPlugin extends SpringPlugin {
17 17  
18   - private static final int PORT = 8092;
19   - private Vertx vertx;
20   -
21 18 public HttpVertxPlugin(PluginWrapper wrapper) {
22 19 super(wrapper);
23 20 }
24 21  
25 22 @Override
26 23 public void start() {
27   - log.info("HttpVertxPlugin.start()");
  24 + log.info("[HttpVertxPlugin] start ...");
28 25  
29   - // 获取 DeviceDataApi 实例
30   - DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class);
31   - if (deviceDataApi == null) {
32   - log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!");
  26 + // 1. 获取插件上下文
  27 + ApplicationContext pluginContext = getApplicationContext();
  28 + if (pluginContext == null) {
  29 + log.error("[HttpVertxPlugin] pluginContext is null, start failed.");
33 30 return;
34 31 }
35 32  
36   - // 初始化 Vert.x
37   - vertx = Vertx.vertx();
38   - Router router = Router.router(vertx);
39   -
40   - // 处理 Body
41   - router.route().handler(BodyHandler.create());
42   -
43   - // 设置路由
44   - router.post("/sys/:productKey/:deviceName/thing/event/property/post")
45   - .handler(new HttpVertxHandler(deviceDataApi));
46   -
47   - // 启动 HTTP 服务器
48   - vertx.createHttpServer()
49   - .requestHandler(router)
50   - .listen(PORT, http -> {
51   - if (http.succeeded()) {
52   - log.info("HTTP 服务器启动成功,端口为: {}", PORT);
53   - } else {
54   - log.error("HTTP 服务器启动失败", http.cause());
55   - }
56   - });
  33 + // 2. 启动 Vertx
  34 + VertxService vertxService = pluginContext.getBean(VertxService.class);
  35 + vertxService.startServer();
57 36 }
58 37  
  38 +
59 39 @Override
60 40 public void stop() {
61   - log.info("HttpVertxPlugin.stop()");
62   - if (vertx != null) {
63   - vertx.close(ar -> {
64   - if (ar.succeeded()) {
65   - log.info("Vert.x 关闭成功");
66   - } else {
67   - log.error("Vert.x 关闭失败", ar.cause());
68   - }
69   - });
  41 + log.info("[HttpVertxPlugin] stop ...");
  42 + ApplicationContext pluginContext = getApplicationContext();
  43 + if (pluginContext != null) {
  44 + // 停止服务器
  45 + VertxService vertxService = pluginContext.getBean(VertxService.class);
  46 + vertxService.stopServer();
70 47 }
71 48 }
72 49  
73 50 @Override
74 51 protected ApplicationContext createApplicationContext() {
75   - AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
76   - applicationContext.setClassLoader(getWrapper().getPluginClassLoader());
77   - applicationContext.refresh();
  52 + AnnotationConfigApplicationContext pluginContext = new AnnotationConfigApplicationContext() {
  53 + @Override
  54 + protected void prepareRefresh() {
  55 + // 在刷新容器前注册主程序中的 Bean
  56 + ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
  57 + DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class);
  58 + beanFactory.registerSingleton("deviceDataApi", deviceDataApi);
  59 +
  60 + super.prepareRefresh();
  61 + }
  62 + };
78 63  
79   - return applicationContext;
  64 + pluginContext.setClassLoader(getWrapper().getPluginClassLoader());
  65 + pluginContext.scan("cn.iocoder.yudao.module.iot.plugin.http.config");
  66 + pluginContext.refresh();
  67 +
  68 + return pluginContext;
80 69 }
81   -}
82 70 \ No newline at end of file
  71 +
  72 +}
... ...
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 1 package cn.iocoder.yudao.module.iot.plugin.http.config;
2 2  
3   -import org.pf4j.DefaultPluginManager;
4   -import org.pf4j.PluginWrapper;
  3 +import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
  4 +import cn.iocoder.yudao.module.iot.plugin.http.service.HttpVertxHandler;
  5 +import io.vertx.core.Vertx;
  6 +import io.vertx.ext.web.Router;
  7 +import io.vertx.ext.web.handler.BodyHandler;
  8 +import lombok.extern.slf4j.Slf4j;
  9 +import org.springframework.beans.factory.annotation.Value;
5 10 import org.springframework.context.annotation.Bean;
6 11 import org.springframework.context.annotation.Configuration;
7 12  
  13 +/**
  14 + * 插件与独立运行都可复用的配置类
  15 + */
  16 +@Slf4j
8 17 @Configuration
9 18 public class HttpVertxPluginConfiguration {
10 19  
11   - @Bean(initMethod = "start")
12   - public HttpVertxPlugin httpVertxPlugin() {
13   - PluginWrapper pluginWrapper = new PluginWrapper(new DefaultPluginManager(), null, null, null);
14   - return new HttpVertxPlugin(pluginWrapper);
  20 + /**
  21 + * 可在 application.yml 中配置,默认端口 8092
  22 + */
  23 + @Value("${plugin.http.server.port:8092}")
  24 + private Integer port;
  25 +
  26 + /**
  27 + * 创建 Vert.x 实例
  28 + */
  29 + @Bean
  30 + public Vertx vertx() {
  31 + return Vertx.vertx();
  32 + }
  33 +
  34 + /**
  35 + * 创建路由
  36 + */
  37 + @Bean
  38 + public Router router(Vertx vertx, HttpVertxHandler httpVertxHandler) {
  39 + Router router = Router.router(vertx);
  40 +
  41 + // 处理 Body
  42 + router.route().handler(BodyHandler.create());
  43 +
  44 + // 设置路由
  45 + router.post("/sys/:productKey/:deviceName/thing/event/property/post")
  46 + .handler(httpVertxHandler);
  47 +
  48 + return router;
  49 + }
  50 +
  51 + /**
  52 + * 注入你的 Http 处理器 Handler,依赖 DeviceDataApi
  53 + */
  54 + @Bean
  55 + public HttpVertxHandler httpVertxHandler(DeviceDataApi deviceDataApi) {
  56 + return new HttpVertxHandler(deviceDataApi);
  57 + }
  58 +
  59 + /**
  60 + * 定义一个 VertxService 来管理服务器启动逻辑
  61 + * 无论是独立运行还是插件方式,都可以共用此类
  62 + */
  63 + @Bean
  64 + public VertxService vertxService(Vertx vertx, Router router) {
  65 + return new VertxService(port, vertx, router);
15 66 }
16   -}
17 67 \ No newline at end of file
  68 +}
... ...
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 → 100644
  1 +package cn.iocoder.yudao.module.iot.plugin.http.config;
  2 +
  3 +import io.vertx.core.Vertx;
  4 +import io.vertx.ext.web.Router;
  5 +import lombok.extern.slf4j.Slf4j;
  6 +
  7 +/**
  8 + * 封装 Vert.x HTTP 服务的启动/关闭逻辑
  9 + */
  10 +@Slf4j
  11 +public class VertxService {
  12 +
  13 + private final Integer port;
  14 + private final Vertx vertx;
  15 + private final Router router;
  16 +
  17 + public VertxService(Integer port, Vertx vertx, Router router) {
  18 + this.port = port;
  19 + this.vertx = vertx;
  20 + this.router = router;
  21 + }
  22 +
  23 + /**
  24 + * 启动 HTTP 服务器
  25 + */
  26 + public void startServer() {
  27 + vertx.createHttpServer()
  28 + .requestHandler(router)
  29 + .listen(port, http -> {
  30 + if (http.succeeded()) {
  31 + log.info("[VertxService] HTTP 服务器启动成功, 端口: {}", port);
  32 + } else {
  33 + log.error("[VertxService] HTTP 服务器启动失败", http.cause());
  34 + }
  35 + });
  36 + }
  37 +
  38 + /**
  39 + * 关闭 HTTP 服务器
  40 + */
  41 + public void stopServer() {
  42 + if (vertx != null) {
  43 + vertx.close(ar -> {
  44 + if (ar.succeeded()) {
  45 + log.info("[VertxService] Vert.x 关闭成功");
  46 + } else {
  47 + log.error("[VertxService] Vert.x 关闭失败", ar.cause());
  48 + }
  49 + });
  50 + }
  51 + }
  52 +}
... ...
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<RoutingContext> {
22 22 public void handle(RoutingContext ctx) {
23 23 String productKey = ctx.pathParam("productKey");
24 24 String deviceName = ctx.pathParam("deviceName");
25   - RequestBody requestBody = ctx.body();
26 25  
  26 + RequestBody requestBody = ctx.body();
27 27 JSONObject jsonData;
28 28 try {
29 29 jsonData = JSONUtil.parseObj(requestBody.asJsonObject());
30 30 } catch (Exception e) {
31   - JSONObject res = createResponseJson(
32   - 400,
33   - new JSONObject(),
34   - null,
35   - "请求数据不是合法的 JSON 格式: " + e.getMessage(),
36   - "thing.event.property.post",
37   - "1.0");
38   - ctx.response()
39   - .setStatusCode(400)
  31 + log.error("[HttpVertxHandler] 请求数据解析失败", e);
  32 + ctx.response().setStatusCode(400)
40 33 .putHeader("Content-Type", "application/json; charset=UTF-8")
41   - .end(res.toString());
  34 + .end(createResponseJson(400, null, null,
  35 + "请求数据不是合法的 JSON 格式: " + e.getMessage(),
  36 + "thing.event.property.post", "1.0").toString());
42 37 return;
43 38 }
44 39  
45   - String id = jsonData.getStr("id", null);
  40 + String id = jsonData.getStr("id");
46 41  
47 42 try {
48   - // 调用主程序的接口保存数据
49   - IotDevicePropertyReportReqDTO createDTO = IotDevicePropertyReportReqDTO.builder()
  43 + IotDevicePropertyReportReqDTO reportReqDTO = IotDevicePropertyReportReqDTO.builder()
50 44 .productKey(productKey)
51 45 .deviceName(deviceName)
52   - .params(jsonData) // TODO 芋艿:这块要优化
  46 + .params(jsonData)
53 47 .build();
54   - deviceDataApi.reportDevicePropertyData(createDTO);
55 48  
56   - // 构造成功响应内容
57   - JSONObject successRes = createResponseJson(
58   - 200,
59   - new JSONObject(),
60   - id,
61   - "success",
62   - "thing.event.property.post",
63   - "1.0");
  49 + deviceDataApi.reportDevicePropertyData(reportReqDTO);
  50 +
64 51 ctx.response()
65 52 .setStatusCode(200)
66 53 .putHeader("Content-Type", "application/json; charset=UTF-8")
67   - .end(successRes.toString());
  54 + .end(createResponseJson(200, new JSONObject(), id, "success",
  55 + "thing.event.property.post", "1.0").toString());
  56 +
68 57 } catch (Exception e) {
69   - JSONObject errorRes = createResponseJson(
70   - 500,
71   - new JSONObject(),
72   - id,
73   - "The format of result is error!",
74   - "thing.event.property.post",
75   - "1.0");
  58 + log.error("[HttpVertxHandler] 上报属性数据失败", e);
76 59 ctx.response()
77 60 .setStatusCode(500)
78 61 .putHeader("Content-Type", "application/json; charset=UTF-8")
79   - .end(errorRes.toString());
  62 + .end(createResponseJson(500, new JSONObject(), id,
  63 + "The format of result is error!",
  64 + "thing.event.property.post", "1.0").toString());
80 65 }
81 66 }
82 67  
83   - /**
84   - * 创建标准化的响应 JSON 对象
85   - *
86   - * @param code 响应状态码(业务层面的)
87   - * @param data 返回的数据对象(JSON)
88   - * @param id 请求的 id(可选)
89   - * @param message 返回的提示信息
90   - * @param method 返回的 method 标识
91   - * @param version 返回的版本号
92   - * @return 构造好的 JSON 对象
93   - */
94   - private JSONObject createResponseJson(int code, JSONObject data, String id, String message, String method,
95   - String version) {
  68 + private JSONObject createResponseJson(int code, JSONObject data, String id,
  69 + String message, String method, String version) {
96 70 JSONObject res = new JSONObject();
97 71 res.set("code", code);
98 72 res.set("data", data != null ? data : new JSONObject());
... ...