【功能优化】IoT:

1. DeviceDataApi => IotDeviceUpstreamApi,并新建 upstream 包
2. ThingModelMessage => IotDeviceMessage 设备消息
3. 基于 spring event 异步消费 IotDeviceMessage,并实现 IotDeviceLogMessageConsumer 记录日志
This commit is contained in:
YunaiV
2025-01-27 14:15:07 +08:00
parent f4ad3e9d2d
commit 8089f3a319
46 changed files with 648 additions and 790 deletions

View File

@@ -1,99 +1,62 @@
package cn.iocoder.yudao.module.iot.plugin.common.api;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
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;
/**
* 用于通过 {@link RestTemplate} 向远程 IoT 服务发送设备数据相关的请求,
* 包括设备状态更新、事件数据上报、属性数据上报等操作。
*/
// TODO @haohao类注释写一下比较好
// TODO @haohao类名要改下
@Slf4j
@RequiredArgsConstructor
public class DeviceDataApiClient implements DeviceDataApi {
public class DeviceDataApiClient implements IotDeviceUpstreamApi {
public static final String URL_PREFIX = "/rpc-api/iot/device/upstream";
/**
* 用于发送 HTTP 请求的工具
*/
private final RestTemplate restTemplate;
/**
* 远程 IoT 服务的基础 URL
* 例如http://127.0.0.1:8080
*/
private final String deviceDataUrl;
// 可以通过构造器把 RestTemplate 和 baseUrl 注入进来
// TODO @haohao可以用 lombok 简化
public DeviceDataApiClient(RestTemplate restTemplate, String deviceDataUrl) {
this.restTemplate = restTemplate;
this.deviceDataUrl = deviceDataUrl;
}
// TODO @haohao返回结果不用 CommonResult 哈。
@Override
public CommonResult<Boolean> updateDeviceStatus(IotDeviceStatusUpdateReqDTO updateReqDTO) {
String url = deviceDataUrl + "/rpc-api/iot/device-data/update-status";
String url = deviceDataUrl + URL_PREFIX + "/update-status";
return doPost(url, updateReqDTO, "updateDeviceStatus");
}
@Override
public CommonResult<Boolean> reportDeviceEventData(IotDeviceEventReportReqDTO reportReqDTO) {
String url = deviceDataUrl + "/rpc-api/iot/device-data/report-event";
String url = deviceDataUrl + URL_PREFIX + "/report-event";
return doPost(url, reportReqDTO, "reportDeviceEventData");
}
@Override
public CommonResult<Boolean> reportDevicePropertyData(IotDevicePropertyReportReqDTO reportReqDTO) {
String url = deviceDataUrl + "/rpc-api/iot/device-data/report-property";
String url = deviceDataUrl + URL_PREFIX + "/report-property";
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<?> 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");
}
// 这里指定返回类型为 CommonResult<?>,根据后台服务返回的实际结构做调整
restTemplate.postForObject(url, requestBody, CommonResult.class);
// TODO @haohaocheck 结果,是否成功
return success(true);
} catch (Exception e) {
log.error("[{}] Error sending request to URL: {}", actionName, url, e);
return CommonResult.error(400, "Request error: " + e.getMessage());

View File

@@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.iot.plugin.common.config;
import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
import cn.iocoder.yudao.module.iot.plugin.common.api.DeviceDataApiClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -43,7 +43,7 @@ public class YudaoDeviceDataApiAutoConfiguration {
* @return DeviceDataApi 实例
*/
@Bean
public DeviceDataApi deviceDataApi(RestTemplate restTemplate) {
public IotDeviceUpstreamApi deviceDataApi(RestTemplate restTemplate) {
return new DeviceDataApiClient(restTemplate, deviceDataUrl);
}

View File

@@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.iot.plugin;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
import lombok.extern.slf4j.Slf4j;
import org.pf4j.Plugin;
import org.pf4j.PluginWrapper;
@@ -27,7 +27,7 @@ public class EmqxPlugin extends Plugin {
executorService = Executors.newSingleThreadExecutor();
}
DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class);
IotDeviceUpstreamApi deviceDataApi = SpringUtil.getBean(IotDeviceUpstreamApi.class);
if (deviceDataApi == null) {
log.error("未能从 ServiceRegistry 获取 DeviceDataApi 实例,请确保主程序已正确注册!");
return;

View File

@@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.plugin.http.config;
import cn.hutool.core.lang.Assert;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
import lombok.extern.slf4j.Slf4j;
import org.pf4j.PluginWrapper;
import org.pf4j.spring.SpringPlugin;
@@ -64,7 +64,7 @@ public class HttpVertxPlugin extends SpringPlugin {
protected void prepareRefresh() {
// 在刷新容器前注册主程序中的 Bean
ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
DeviceDataApi deviceDataApi = SpringUtil.getBean(DeviceDataApi.class);
IotDeviceUpstreamApi deviceDataApi = SpringUtil.getBean(IotDeviceUpstreamApi.class);
beanFactory.registerSingleton("deviceDataApi", deviceDataApi);
super.prepareRefresh();
}

View File

@@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.iot.plugin.http.config;
import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
import cn.iocoder.yudao.module.iot.plugin.http.service.HttpVertxHandler;
import io.vertx.core.Vertx;
import io.vertx.ext.web.Router;
@@ -61,7 +61,7 @@ public class HttpVertxPluginConfiguration {
* @return HttpVertxHandler 实例
*/
@Bean
public HttpVertxHandler httpVertxHandler(DeviceDataApi deviceDataApi) {
public HttpVertxHandler httpVertxHandler(IotDeviceUpstreamApi deviceDataApi) {
return new HttpVertxHandler(deviceDataApi);
}

View File

@@ -2,19 +2,21 @@ package cn.iocoder.yudao.module.iot.plugin.http.service;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi;
import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO;
import io.vertx.core.Handler;
import io.vertx.ext.web.RequestBody;
import io.vertx.ext.web.RoutingContext;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
@Slf4j
public class HttpVertxHandler implements Handler<RoutingContext> {
private final DeviceDataApi deviceDataApi;
private final IotDeviceUpstreamApi deviceDataApi;
public HttpVertxHandler(DeviceDataApi deviceDataApi) {
public HttpVertxHandler(IotDeviceUpstreamApi deviceDataApi) {
this.deviceDataApi = deviceDataApi;
}
@@ -23,6 +25,7 @@ public class HttpVertxHandler implements Handler<RoutingContext> {
String productKey = ctx.pathParam("productKey");
String deviceName = ctx.pathParam("deviceName");
// TODO @haohaorequestBody.asJsonObject() 貌似天然就是 json 对象哈?
RequestBody requestBody = ctx.body();
JSONObject jsonData;
try {
@@ -43,7 +46,7 @@ public class HttpVertxHandler implements Handler<RoutingContext> {
IotDevicePropertyReportReqDTO reportReqDTO = IotDevicePropertyReportReqDTO.builder()
.productKey(productKey)
.deviceName(deviceName)
.params(jsonData)
.properties((Map<String, Object>) requestBody.asJsonObject().getMap().get("properties"))
.build();
deviceDataApi.reportDevicePropertyData(reportReqDTO);