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 package cn.iocoder.yudao.module.iot.plugin.http; 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 import org.springframework.boot.SpringApplication; 5 import org.springframework.boot.SpringApplication;
4 import org.springframework.boot.WebApplicationType; 6 import org.springframework.boot.WebApplicationType;
5 import org.springframework.boot.autoconfigure.SpringBootApplication; 7 import org.springframework.boot.autoconfigure.SpringBootApplication;
  8 +import org.springframework.context.ConfigurableApplicationContext;
6 9
  10 +/**
  11 + * 独立运行入口
  12 + */
  13 +@Slf4j
7 @SpringBootApplication(scanBasePackages = "cn.iocoder.yudao.module.iot.plugin") 14 @SpringBootApplication(scanBasePackages = "cn.iocoder.yudao.module.iot.plugin")
8 public class HttpPluginSpringbootApplication { 15 public class HttpPluginSpringbootApplication {
9 16
10 public static void main(String[] args) { 17 public static void main(String[] args) {
  18 + // 这里可选择 NONE / SERVLET / REACTIVE,看你需求
11 SpringApplication application = new SpringApplication(HttpPluginSpringbootApplication.class); 19 SpringApplication application = new SpringApplication(HttpPluginSpringbootApplication.class);
12 application.setWebApplicationType(WebApplicationType.NONE); 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 \ No newline at end of file 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,80 +2,70 @@ package cn.iocoder.yudao.module.iot.plugin.http.config;
2 2
3 import cn.hutool.extra.spring.SpringUtil; 3 import cn.hutool.extra.spring.SpringUtil;
4 import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; 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 import lombok.extern.slf4j.Slf4j; 5 import lombok.extern.slf4j.Slf4j;
10 import org.pf4j.PluginWrapper; 6 import org.pf4j.PluginWrapper;
11 import org.pf4j.spring.SpringPlugin; 7 import org.pf4j.spring.SpringPlugin;
  8 +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
12 import org.springframework.context.ApplicationContext; 9 import org.springframework.context.ApplicationContext;
13 import org.springframework.context.annotation.AnnotationConfigApplicationContext; 10 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
14 11
  12 +/**
  13 + * 负责插件的启动和停止,与 Vert.x 的生命周期管理
  14 + */
15 @Slf4j 15 @Slf4j
16 public class HttpVertxPlugin extends SpringPlugin { 16 public class HttpVertxPlugin extends SpringPlugin {
17 17
18 - private static final int PORT = 8092;  
19 - private Vertx vertx;  
20 -  
21 public HttpVertxPlugin(PluginWrapper wrapper) { 18 public HttpVertxPlugin(PluginWrapper wrapper) {
22 super(wrapper); 19 super(wrapper);
23 } 20 }
24 21
25 @Override 22 @Override
26 public void start() { 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 return; 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 @Override 39 @Override
60 public void stop() { 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 @Override 50 @Override
74 protected ApplicationContext createApplicationContext() { 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 \ No newline at end of file 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 package cn.iocoder.yudao.module.iot.plugin.http.config; 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 import org.springframework.context.annotation.Bean; 10 import org.springframework.context.annotation.Bean;
6 import org.springframework.context.annotation.Configuration; 11 import org.springframework.context.annotation.Configuration;
7 12
  13 +/**
  14 + * 插件与独立运行都可复用的配置类
  15 + */
  16 +@Slf4j
8 @Configuration 17 @Configuration
9 public class HttpVertxPluginConfiguration { 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 \ No newline at end of file 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,77 +22,51 @@ public class HttpVertxHandler implements Handler<RoutingContext> {
22 public void handle(RoutingContext ctx) { 22 public void handle(RoutingContext ctx) {
23 String productKey = ctx.pathParam("productKey"); 23 String productKey = ctx.pathParam("productKey");
24 String deviceName = ctx.pathParam("deviceName"); 24 String deviceName = ctx.pathParam("deviceName");
25 - RequestBody requestBody = ctx.body();  
26 25
  26 + RequestBody requestBody = ctx.body();
27 JSONObject jsonData; 27 JSONObject jsonData;
28 try { 28 try {
29 jsonData = JSONUtil.parseObj(requestBody.asJsonObject()); 29 jsonData = JSONUtil.parseObj(requestBody.asJsonObject());
30 } catch (Exception e) { 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 .putHeader("Content-Type", "application/json; charset=UTF-8") 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 return; 37 return;
43 } 38 }
44 39
45 - String id = jsonData.getStr("id", null); 40 + String id = jsonData.getStr("id");
46 41
47 try { 42 try {
48 - // 调用主程序的接口保存数据  
49 - IotDevicePropertyReportReqDTO createDTO = IotDevicePropertyReportReqDTO.builder() 43 + IotDevicePropertyReportReqDTO reportReqDTO = IotDevicePropertyReportReqDTO.builder()
50 .productKey(productKey) 44 .productKey(productKey)
51 .deviceName(deviceName) 45 .deviceName(deviceName)
52 - .params(jsonData) // TODO 芋艿:这块要优化 46 + .params(jsonData)
53 .build(); 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 ctx.response() 51 ctx.response()
65 .setStatusCode(200) 52 .setStatusCode(200)
66 .putHeader("Content-Type", "application/json; charset=UTF-8") 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 } catch (Exception e) { 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 ctx.response() 59 ctx.response()
77 .setStatusCode(500) 60 .setStatusCode(500)
78 .putHeader("Content-Type", "application/json; charset=UTF-8") 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 JSONObject res = new JSONObject(); 70 JSONObject res = new JSONObject();
97 res.set("code", code); 71 res.set("code", code);
98 res.set("data", data != null ? data : new JSONObject()); 72 res.set("data", data != null ? data : new JSONObject());