【功能完善】IoT: 删除旧版 HTTP 插件,重构 HttpPlugin 以支持独立启动,新增 VertxService 管理 HTTP 服务器逻辑,优化代码结构和异常处理,更新相关配置以提升插件性能和可维护性。
This commit is contained in:
parent
698cec92bd
commit
88ef8ba2e3
Binary file not shown.
Binary file not shown.
|
@ -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] 独立模式启动完成");
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建路由
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,77 +22,51 @@ public class HttpVertxHandler implements Handler<RoutingContext> {
|
|||
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());
|
||||
|
|
Loading…
Reference in New Issue