【代码优化】重构 HTTP插件并添加自动配置

This commit is contained in:
安浩浩
2025-01-26 17:29:03 +08:00
parent 269dec1b2e
commit 7bfa830628
10 changed files with 204 additions and 109 deletions

View File

@@ -5,24 +5,30 @@ import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceEventReportReqDTO;
import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStatusUpdateReqDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.client.RestTemplate;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
// TODO @haohao类注释写一下比较好
/**
* 用于通过 {@link RestTemplate} 向远程 IoT 服务发送设备数据相关的请求,
* 包括设备状态更新、事件数据上报、属性数据上报等操作。
*/
@Slf4j
@RequiredArgsConstructor
public class DeviceDataApiClient implements DeviceDataApi {
/**
* 用于发送 HTTP 请求的工具
*/
private final RestTemplate restTemplate;
private final String deviceDataUrl;
// 可以通过构造器把 RestTemplate 和 baseUrl 注入进来
// TODO @haohao可以用 lombok 简化
public DeviceDataApiClient(RestTemplate restTemplate, String deviceDataUrl) {
this.restTemplate = restTemplate;
this.deviceDataUrl = deviceDataUrl;
}
/**
* 远程 IoT 服务的基础 URL
* 例如http://127.0.0.1:8080
*/
private final String deviceDataUrl;
// TODO @haohao返回结果不用 CommonResult 哈。
@Override
@@ -43,17 +49,51 @@ public class DeviceDataApiClient implements DeviceDataApi {
return doPost(url, reportReqDTO, "reportDevicePropertyData");
}
// TODO @haohao未来可能有 get 类型哈
/**
* 将与远程服务交互的通用逻辑抽取成一个私有方法
* 发送 GET 请求
*
* @param <T> 请求体类型
* @param url 请求 URL
* @param requestBody 请求体
* @param actionName 操作名称
* @return 响应结果
*/
private <T> CommonResult<Boolean> doGet(String url, T requestBody, String actionName) {
log.info("[{}] Sending request to URL: {}", actionName, url);
try {
CommonResult<?> response = restTemplate.getForObject(url, CommonResult.class);
if (response != null && response.isSuccess()) {
return success(true);
} else {
log.warn("[{}] Request to URL: {} failed with response: {}", actionName, url, response);
return CommonResult.error(500, "Request failed");
}
} catch (Exception e) {
log.error("[{}] Error sending request to URL: {}", actionName, url, e);
return CommonResult.error(400, "Request error: " + e.getMessage());
}
}
/**
* 发送 POST 请求
*
* @param <T> 请求体类型
* @param url 请求 URL
* @param requestBody 请求体
* @param actionName 操作名称
* @return 响应结果
*/
private <T> CommonResult<Boolean> doPost(String url, T requestBody, String actionName) {
log.info("[{}] Sending request to URL: {}", actionName, url);
try {
// 这里指定返回类型为 CommonResult<?>,根据后台服务返回的实际结构做调整
restTemplate.postForObject(url, requestBody, CommonResult.class);
// TODO @haohaocheck 结果,是否成功
return success(true);
CommonResult<?> response = restTemplate.postForObject(url, requestBody, CommonResult.class);
if (response != null && response.isSuccess()) {
return success(true);
} else {
log.warn("[{}] Request to URL: {} failed with response: {}", actionName, url, response);
return CommonResult.error(500, "Request failed");
}
} catch (Exception e) {
log.error("[{}] Error sending request to URL: {}", actionName, url, e);
return CommonResult.error(400, "Request error: " + e.getMessage());

View File

@@ -1,31 +0,0 @@
package cn.iocoder.yudao.module.iot.plugin.common.config;
import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
import cn.iocoder.yudao.module.iot.plugin.common.api.DeviceDataApiClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
// TODO @haohao这个最好是 autoconfiguration
@Configuration
public class DeviceDataApiInitializer {
// TODO @haohao这个要不搞个配置类哈
@Value("${iot.device-data.url}")
private String deviceDataUrl;
@Bean
public RestTemplate restTemplate() {
// TODO haohao如果你有更多的自定义需求比如连接池、超时时间等可以在这里设置
return new RestTemplateBuilder().build();
}
// TODO @haohao不存在时才构建
@Bean
public DeviceDataApi deviceDataApi(RestTemplate restTemplate) {
return new DeviceDataApiClient(restTemplate, deviceDataUrl);
}
}

View File

@@ -0,0 +1,51 @@
package cn.iocoder.yudao.module.iot.plugin.common.config;
import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
import cn.iocoder.yudao.module.iot.plugin.common.api.DeviceDataApiClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
import java.time.Duration;
/**
* 设备数据 API 初始化器
*
* @author haohao
*/
@AutoConfiguration
public class YudaoDeviceDataApiAutoConfiguration {
// TODO @haohao这个要不搞个配置类哈
@Value("${iot.device-data.url}")
private String deviceDataUrl;
/**
* 创建 RestTemplate 实例
*
* @return RestTemplate 实例
*/
@Bean
public RestTemplate restTemplate() {
// 如果你有更多的自定义需求,比如连接池、超时时间等,可以在这里设置
return new RestTemplateBuilder()
.setConnectTimeout(Duration.ofMillis(5000)) // 设置连接超时时间
.setReadTimeout(Duration.ofMillis(5000)) // 设置读取超时时间
.build();
}
/**
* 创建 DeviceDataApi 实例
*
* @param restTemplate RestTemplate 实例
* @return DeviceDataApi 实例
*/
@Bean
public DeviceDataApi deviceDataApi(RestTemplate restTemplate) {
return new DeviceDataApiClient(restTemplate, deviceDataUrl);
}
}

View File

@@ -0,0 +1 @@
cn.iocoder.yudao.module.iot.plugin.common.config.YudaoDeviceDataApiAutoConfiguration

View File

@@ -11,7 +11,7 @@ import org.springframework.context.ConfigurableApplicationContext;
* 独立运行入口
*/
@Slf4j
@SpringBootApplication(scanBasePackages = "cn.iocoder.yudao.module.iot.plugin") // TODO @haohao建议不扫描 cn.iocoder.yudao.module.iot.plugin而是通过自动配置初始化 common 的
@SpringBootApplication
public class HttpPluginSpringbootApplication {
public static void main(String[] args) {
@@ -21,6 +21,7 @@ public class HttpPluginSpringbootApplication {
// 手动获取 VertxService 并启动
// TODO @haohao可以放在 bean 的 init 里么?
// 会和插件模式冲突
VertxService vertxService = context.getBean(VertxService.class);
vertxService.startServer();

View File

@@ -21,30 +21,41 @@ public class HttpVertxPlugin extends SpringPlugin {
@Override
public void start() {
// TODO @haohao这种最好启动中启动完成成对打印日志方便定位问题
log.info("[HttpVertxPlugin][start ...]");
log.info("[HttpVertxPlugin][start][begin] 开始启动 HttpVertxPlugin 插件...");
// 1. 获取插件上下文
ApplicationContext pluginContext = getApplicationContext();
if (pluginContext == null) {
log.error("[HttpVertxPlugin] pluginContext is null, start failed.");
return;
try {
// 1. 获取插件上下文
ApplicationContext pluginContext = getApplicationContext();
if (pluginContext == null) {
log.error("[HttpVertxPlugin][start][fail] pluginContext is null, 启动失败!");
return;
}
// 2. 启动 Vert.x
VertxService vertxService = pluginContext.getBean(VertxService.class);
vertxService.startServer();
log.info("[HttpVertxPlugin][start][end] 启动完成");
} catch (Exception e) {
log.error("[HttpVertxPlugin][start][exception] 启动过程出现异常!", e);
}
// 2. 启动 Vertx
VertxService vertxService = pluginContext.getBean(VertxService.class);
vertxService.startServer();
}
@Override
public void stop() {
log.info("[HttpVertxPlugin][stop ...]");
ApplicationContext pluginContext = getApplicationContext();
if (pluginContext != null) {
// 停止服务器
VertxService vertxService = pluginContext.getBean(VertxService.class);
vertxService.stopServer();
log.info("[HttpVertxPlugin][stop][begin] 开始停止 HttpVertxPlugin 插件...");
try {
ApplicationContext pluginContext = getApplicationContext();
if (pluginContext != null) {
// 停止服务器
VertxService vertxService = pluginContext.getBean(VertxService.class);
vertxService.stopServer();
}
log.info("[HttpVertxPlugin][stop][end] 停止完成");
} catch (Exception e) {
log.error("[HttpVertxPlugin][stop][exception] 停止过程出现异常!", e);
}
}
@@ -68,5 +79,4 @@ public class HttpVertxPlugin extends SpringPlugin {
return pluginContext;
}
}
}

View File

@@ -34,6 +34,10 @@ public class HttpVertxPluginConfiguration {
/**
* 创建路由
*
* @param vertx Vertx 实例
* @param httpVertxHandler HttpVertxHandler 实例
* @return Router 实例
*/
@Bean
public Router router(Vertx vertx, HttpVertxHandler httpVertxHandler) {
@@ -50,6 +54,12 @@ public class HttpVertxPluginConfiguration {
return router;
}
/**
* 创建 HttpVertxHandler 实例
*
* @param deviceDataApi DeviceDataApi 实例
* @return HttpVertxHandler 实例
*/
@Bean
public HttpVertxHandler httpVertxHandler(DeviceDataApi deviceDataApi) {
return new HttpVertxHandler(deviceDataApi);
@@ -58,6 +68,10 @@ public class HttpVertxPluginConfiguration {
/**
* 定义一个 VertxService 来管理服务器启动逻辑
* 无论是独立运行还是插件方式,都可以共用此类
*
* @param vertx Vertx 实例
* @param router Router 实例
* @return VertxService 实例
*/
@Bean
public VertxService vertxService(Vertx vertx, Router router) {

View File

@@ -5,3 +5,8 @@ spring:
iot:
device-data:
url: http://127.0.0.1:48080
plugin:
http:
server:
port: 8092