From 2d18e218c7dfb6f21c23c99c19387309f2d52238 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 30 Jan 2025 18:10:46 +0800 Subject: [PATCH] 【代码新增】IoT:增加 IotDeviceDownstreamHandler 接口的定义,以及 IotDeviceDownstreamServer 的部分实现 --- yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java | 6 +++--- yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java | 25 ------------------------- yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java | 20 -------------------- yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStateUpdateReqDTO.java | 21 --------------------- yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceUpstreamAbstractReqDTO.java | 45 --------------------------------------------- yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceDownstreamAbstractReqDTO.java | 30 ++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertyGetReqDTO.java | 22 ++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertySetReqDTO.java | 22 ++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceServiceInvokeReqDTO.java | 12 ++++++++++++ yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEventReportReqDTO.java | 25 +++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDevicePropertyReportReqDTO.java | 20 ++++++++++++++++++++ yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceStateUpdateReqDTO.java | 21 +++++++++++++++++++++ yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceUpstreamAbstractReqDTO.java | 45 +++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/package-info.java | 4 ++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java | 6 +++--- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/device/IotDeviceOfflineCheckJob.java | 2 +- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceOnlineMessageConsumer.java | 2 +- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java | 6 +++--- yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java | 8 ++++---- yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/pom.xml | 28 ++++++++++++++-------------- yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java | 66 ------------------------------------------------------------------ yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java | 17 +++++++++++++---- yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/downstream/IotDeviceDownstreamHandler.java | 23 +++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/downstream/IotDeviceDownstreamServer.java | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/downstream/router/IotDeviceServiceInvokeVertxHandler.java | 23 +++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/upstream/IotDeviceUpstreamClient.java | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/pom.xml | 8 ++++++++ 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 | 6 +++--- yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/framework/downstream/IotDeviceDownstreamHandlerImpl.java | 34 ++++++++++++++++++++++++++++++++++ yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/framework/package-info.java | 1 + yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/framework/upstream/HttpVertxHandler.java | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 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 | 93 --------------------------------------------------------------------------------------------- 33 files changed, 621 insertions(+), 306 deletions(-) delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStateUpdateReqDTO.java delete mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceUpstreamAbstractReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceDownstreamAbstractReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertyGetReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertySetReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceServiceInvokeReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEventReportReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDevicePropertyReportReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceStateUpdateReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceUpstreamAbstractReqDTO.java create mode 100644 yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/package-info.java delete mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/downstream/IotDeviceDownstreamHandler.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/downstream/IotDeviceDownstreamServer.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/downstream/router/IotDeviceServiceInvokeVertxHandler.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/upstream/IotDeviceUpstreamClient.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/framework/downstream/IotDeviceDownstreamHandlerImpl.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/framework/package-info.java create mode 100644 yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/framework/upstream/HttpVertxHandler.java delete mode 100644 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 diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java index 2af479e..b1a59f6 100644 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/IotDeviceUpstreamApi.java @@ -1,9 +1,9 @@ package cn.iocoder.yudao.module.iot.api.device; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -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.IotDeviceStateUpdateReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; import cn.iocoder.yudao.module.iot.enums.ApiConstants; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.PostMapping; diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java deleted file mode 100644 index 6eacde2..0000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceEventReportReqDTO.java +++ /dev/null @@ -1,25 +0,0 @@ -package cn.iocoder.yudao.module.iot.api.device.dto; - -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -import java.util.Map; - -/** - * IoT 设备【事件】数据上报 Request DTO - */ -@Data -public class IotDeviceEventReportReqDTO extends IotDeviceUpstreamAbstractReqDTO { - - /** - * 事件标识 - */ - @NotEmpty(message = "事件标识不能为空") - private String identifier; - /** - * 事件参数 - */ - @NotEmpty(message = "事件参数不能为空") - private Map params; - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java deleted file mode 100644 index b848dfb..0000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDevicePropertyReportReqDTO.java +++ /dev/null @@ -1,20 +0,0 @@ -package cn.iocoder.yudao.module.iot.api.device.dto; - -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -import java.util.Map; - -/** - * IoT 设备【属性】数据上报 Request DTO - */ -@Data -public class IotDevicePropertyReportReqDTO extends IotDeviceUpstreamAbstractReqDTO { - - /** - * 属性参数 - */ - @NotEmpty(message = "属性参数不能为空") - private Map properties; - -} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStateUpdateReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStateUpdateReqDTO.java deleted file mode 100644 index 94d5b49..0000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceStateUpdateReqDTO.java +++ /dev/null @@ -1,21 +0,0 @@ -package cn.iocoder.yudao.module.iot.api.device.dto; - -import cn.iocoder.yudao.framework.common.validation.InEnum; -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -/** - * IoT 设备状态更新 Request DTO - */ -@Data -public class IotDeviceStateUpdateReqDTO extends IotDeviceUpstreamAbstractReqDTO { - - /** - * 设备状态 - */ - @NotNull(message = "设备状态不能为空") - @InEnum(IotDeviceStateEnum.class) // 只使用:在线、离线 - private Integer state; - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceUpstreamAbstractReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceUpstreamAbstractReqDTO.java deleted file mode 100644 index 1caf422..0000000 --- a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/IotDeviceUpstreamAbstractReqDTO.java +++ /dev/null @@ -1,45 +0,0 @@ -package cn.iocoder.yudao.module.iot.api.device.dto; - -import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeSerializer; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * IoT 设备上行的抽象 Request DTO - * - * @author 芋道源码 - */ -@Data -public abstract class IotDeviceUpstreamAbstractReqDTO { - - /** - * 请求编号 - */ - private String requestId; - - /** - * 插件标识 - */ - private String pluginKey; - - /** - * 产品标识 - */ - @NotEmpty(message = "产品标识不能为空") - private String productKey; - /** - * 设备名称 - */ - @NotEmpty(message = "设备名称不能为空") - private String deviceName; - - /** - * 上报时间 - */ - @JsonSerialize(using = TimestampLocalDateTimeSerializer.class) // 解决 iot plugins 序列化 LocalDateTime 是数组,导致无法解析的问题 - private LocalDateTime reportTime; - -} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceDownstreamAbstractReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceDownstreamAbstractReqDTO.java new file mode 100644 index 0000000..e78bea6 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceDownstreamAbstractReqDTO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +/** + * IoT 设备下行的抽象 Request DTO + * + * @author 芋道源码 + */ +@Data +public abstract class IotDeviceDownstreamAbstractReqDTO { + + /** + * 请求编号 + */ + private String requestId; + + /** + * 产品标识 + */ + @NotEmpty(message = "产品标识不能为空") + private String productKey; + /** + * 设备名称 + */ + @NotEmpty(message = "设备名称不能为空") + private String deviceName; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertyGetReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertyGetReqDTO.java new file mode 100644 index 0000000..061de57 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertyGetReqDTO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.List; + +/** + * IoT 设备【属性】获取 Request DTO + * + * @author 芋道源码 + */ +@Data +public class IotDevicePropertyGetReqDTO extends IotDeviceDownstreamAbstractReqDTO { + + /** + * 属性标识数组 + */ + @NotEmpty(message = "属性标识数组不能为空") + private List identifiers; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertySetReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertySetReqDTO.java new file mode 100644 index 0000000..170fe80 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDevicePropertySetReqDTO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.Map; + +/** + * IoT 设备【属性】设置 Request DTO + * + * @author 芋道源码 + */ +@Data +public class IotDevicePropertySetReqDTO extends IotDeviceDownstreamAbstractReqDTO { + + /** + * 属性参数 + */ + @NotEmpty(message = "属性参数不能为空") + private Map properties; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceServiceInvokeReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceServiceInvokeReqDTO.java new file mode 100644 index 0000000..ea2ec39 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/downstream/IotDeviceServiceInvokeReqDTO.java @@ -0,0 +1,12 @@ +package cn.iocoder.yudao.module.iot.api.device.dto.control.downstream; + +import lombok.Data; + +/** + * IoT 设备【服务】调用 Request DTO + * + * @author 芋道源码 + */ +@Data +public class IotDeviceServiceInvokeReqDTO extends IotDeviceDownstreamAbstractReqDTO { +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEventReportReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEventReportReqDTO.java new file mode 100644 index 0000000..ee054a2 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceEventReportReqDTO.java @@ -0,0 +1,25 @@ +package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.Map; + +/** + * IoT 设备【事件】上报 Request DTO + */ +@Data +public class IotDeviceEventReportReqDTO extends IotDeviceUpstreamAbstractReqDTO { + + /** + * 事件标识 + */ + @NotEmpty(message = "事件标识不能为空") + private String identifier; + /** + * 事件参数 + */ + @NotEmpty(message = "事件参数不能为空") + private Map params; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDevicePropertyReportReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDevicePropertyReportReqDTO.java new file mode 100644 index 0000000..c1654e6 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDevicePropertyReportReqDTO.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.Map; + +/** + * IoT 设备【属性】上报 Request DTO + */ +@Data +public class IotDevicePropertyReportReqDTO extends IotDeviceUpstreamAbstractReqDTO { + + /** + * 属性参数 + */ + @NotEmpty(message = "属性参数不能为空") + private Map properties; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceStateUpdateReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceStateUpdateReqDTO.java new file mode 100644 index 0000000..02852cf --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceStateUpdateReqDTO.java @@ -0,0 +1,21 @@ +package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; + +import cn.iocoder.yudao.framework.common.validation.InEnum; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * IoT 设备【状态】更新 Request DTO + */ +@Data +public class IotDeviceStateUpdateReqDTO extends IotDeviceUpstreamAbstractReqDTO { + + /** + * 设备状态 + */ + @NotNull(message = "设备状态不能为空") + @InEnum(IotDeviceStateEnum.class) // 只使用:在线、离线 + private Integer state; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceUpstreamAbstractReqDTO.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceUpstreamAbstractReqDTO.java new file mode 100644 index 0000000..abf3a8e --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/control/upstream/IotDeviceUpstreamAbstractReqDTO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.iot.api.device.dto.control.upstream; + +import cn.iocoder.yudao.framework.common.util.json.databind.TimestampLocalDateTimeSerializer; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * IoT 设备上行的抽象 Request DTO + * + * @author 芋道源码 + */ +@Data +public abstract class IotDeviceUpstreamAbstractReqDTO { + + /** + * 请求编号 + */ + private String requestId; + + /** + * 插件标识 + */ + private String pluginKey; + + /** + * 产品标识 + */ + @NotEmpty(message = "产品标识不能为空") + private String productKey; + /** + * 设备名称 + */ + @NotEmpty(message = "设备名称不能为空") + private String deviceName; + + /** + * 上报时间 + */ + @JsonSerialize(using = TimestampLocalDateTimeSerializer.class) // 解决 iot plugins 序列化 LocalDateTime 是数组,导致无法解析的问题 + private LocalDateTime reportTime; + +} diff --git a/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/package-info.java b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/package-info.java new file mode 100644 index 0000000..cb946cd --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/api/device/dto/package-info.java @@ -0,0 +1,4 @@ +/** + * TODO 芋艿:占位 + */ +package cn.iocoder.yudao.module.iot.api.device.dto; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java index a4b1e0a..e86ec5a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceUpstreamApiImpl.java @@ -1,9 +1,9 @@ package cn.iocoder.yudao.module.iot.api.device; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -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.IotDeviceStateUpdateReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; import cn.iocoder.yudao.module.iot.service.device.control.IotDeviceUpstreamService; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/device/IotDeviceOfflineCheckJob.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/device/IotDeviceOfflineCheckJob.java index 4f2cdba..4e9e9ec 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/device/IotDeviceOfflineCheckJob.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/device/IotDeviceOfflineCheckJob.java @@ -5,7 +5,7 @@ import cn.hutool.core.util.IdUtil; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; -import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStateUpdateReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceOnlineMessageConsumer.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceOnlineMessageConsumer.java index 77a17b1..f0e49bd 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceOnlineMessageConsumer.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/mq/consumer/device/IotDeviceOnlineMessageConsumer.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.iot.mq.consumer.device; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStateUpdateReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java index ec7682d..792b754 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceDownstreamServiceImpl.java @@ -1,10 +1,17 @@ package cn.iocoder.yudao.module.iot.service.device.control; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationUpstreamReqVO; +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageTypeEnum; +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; +import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import java.util.Objects; + /** * 设备下行 Service 实现类 * @@ -15,9 +22,53 @@ import org.springframework.validation.annotation.Validated; @Slf4j public class IotDeviceDownstreamServiceImpl implements IotDeviceDownstreamService { + @Resource + private IotDeviceService deviceService; + @Override public void simulationDeviceDownstream(IotDeviceSimulationUpstreamReqVO downstreamReqVO) { + // 校验设备是否存在 + IotDeviceDO device = deviceService.validateDeviceExists(downstreamReqVO.getId()); + // TODO 芋艿:父设备的处理 + IotDeviceDO parentDevice = null; + + // 服务调用 + if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.SERVICE.getType())) { + invokeDeviceService(downstreamReqVO, device, parentDevice); + return; + } + // 属性相关 + if (Objects.equals(downstreamReqVO.getType(), IotDeviceMessageTypeEnum.PROPERTY.getType())) + // 属性设置 + if (Objects.equals(downstreamReqVO.getIdentifier(), IotDeviceMessageIdentifierEnum.PROPERTY_SET.getIdentifier())) { + setDeviceProperty(downstreamReqVO, device, parentDevice); + return; + } + // 属性设置 + if (Objects.equals(downstreamReqVO.getIdentifier(), IotDeviceMessageIdentifierEnum.PROPERTY_GET.getIdentifier())) { + getDeviceProperty(downstreamReqVO, device, parentDevice); + return; + } + // TODO 芋艿:ota 升级 + // TODO 芋艿:配置下发 + } + + private void invokeDeviceService(IotDeviceSimulationUpstreamReqVO downstreamReqVO, + IotDeviceDO device, IotDeviceDO parentDevice) { + // 校验服务是否存在 + // TODO 芋艿:这里需要校验服务是否存在 + // 调用服务 + // TODO 芋艿:这里需要调用服务 + } + + private void setDeviceProperty(IotDeviceSimulationUpstreamReqVO downstreamReqVO, + IotDeviceDO device, IotDeviceDO parentDevice) { + // TODO 芋艿:这里需要设置设备属性 + } + private void getDeviceProperty(IotDeviceSimulationUpstreamReqVO downstreamReqVO, + IotDeviceDO device, IotDeviceDO parentDevice) { + // TODO 芋艿:这里需要获取设备属性 } } diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java index 90e7270..592fa7d 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamService.java @@ -1,8 +1,8 @@ package cn.iocoder.yudao.module.iot.service.device.control; -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.IotDeviceStateUpdateReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationUpstreamReqVO; import jakarta.validation.Valid; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java index eb44353..6667c39 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/control/IotDeviceUpstreamServiceImpl.java @@ -7,10 +7,10 @@ import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; -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.IotDeviceStateUpdateReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceUpstreamAbstractReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceUpstreamAbstractReqDTO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.control.IotDeviceSimulationUpstreamReqVO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceMessageIdentifierEnum; diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/pom.xml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/pom.xml index 676b25f..c02d266 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/pom.xml @@ -18,6 +18,7 @@ + cn.iocoder.boot yudao-common @@ -29,27 +30,26 @@ ${revision} - - org.springframework.boot - spring-boot-starter-web + org.projectlombok + lombok - - org.pf4j - pf4j-spring - - - org.slf4j - slf4j-log4j12 - - + org.springframework + spring-web + - org.projectlombok - lombok + org.springframework.boot + spring-boot-starter + + + + + io.vertx + vertx-web diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java deleted file mode 100644 index a6bdad9..0000000 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/api/DeviceDataApiClient.java +++ /dev/null @@ -1,66 +0,0 @@ -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.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.IotDeviceStateUpdateReqDTO; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.client.RestTemplate; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -// TODO @haohao:类注释,写一下,比较好 -// TODO @haohao:类名要改下 -@Slf4j -public class DeviceDataApiClient implements IotDeviceUpstreamApi { - - public static final String URL_PREFIX = "/rpc-api/iot/device/upstream"; - - 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; - } - - // TODO @haohao:返回结果,不用 CommonResult 哈。 - @Override - public CommonResult updateDeviceState(IotDeviceStateUpdateReqDTO updateReqDTO) { - String url = deviceDataUrl + URL_PREFIX + "/update-state"; - return doPost(url, updateReqDTO, "updateDeviceState"); - } - - @Override - public CommonResult reportDeviceEvent(IotDeviceEventReportReqDTO reportReqDTO) { - String url = deviceDataUrl + URL_PREFIX + "/report-event"; - return doPost(url, reportReqDTO, "reportDeviceEventData"); - } - - @Override - public CommonResult reportDeviceProperty(IotDevicePropertyReportReqDTO reportReqDTO) { - String url = deviceDataUrl + URL_PREFIX + "/report-property"; - return doPost(url, reportReqDTO, "reportDevicePropertyData"); - } - - // TODO @haohao:未来可能有 get 类型哈 - /** - * 将与远程服务交互的通用逻辑抽取成一个私有方法 - */ - private CommonResult doPost(String url, T requestBody, String actionName) { - log.info("[{}] Sending request to URL: {}", actionName, url); - try { - // 这里指定返回类型为 CommonResult,根据后台服务返回的实际结构做调整 - restTemplate.postForObject(url, requestBody, CommonResult.class); - // TODO @haohao:check 结果,是否成功 - return success(true); - } catch (Exception e) { - log.error("[{}] Error sending request to URL: {}", actionName, url, e); - return CommonResult.error(400, "Request error: " + e.getMessage()); - } - } - -} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java index ef613d5..605a903 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/config/YudaoDeviceDataApiAutoConfiguration.java @@ -1,7 +1,9 @@ package cn.iocoder.yudao.module.iot.plugin.common.config; import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; -import cn.iocoder.yudao.module.iot.plugin.common.api.DeviceDataApiClient; +import cn.iocoder.yudao.module.iot.plugin.common.core.downstream.IotDeviceDownstreamHandler; +import cn.iocoder.yudao.module.iot.plugin.common.core.downstream.IotDeviceDownstreamServer; +import cn.iocoder.yudao.module.iot.plugin.common.core.upstream.IotDeviceUpstreamClient; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.web.client.RestTemplateBuilder; @@ -10,6 +12,7 @@ import org.springframework.web.client.RestTemplate; import java.time.Duration; +// TODO @芋艿:配置类的名字 /** * 设备数据 API 初始化器 * @@ -31,8 +34,8 @@ public class YudaoDeviceDataApiAutoConfiguration { public RestTemplate restTemplate() { // 如果你有更多的自定义需求,比如连接池、超时时间等,可以在这里设置 return new RestTemplateBuilder() - .setConnectTimeout(Duration.ofMillis(5000)) // 设置连接超时时间 - .setReadTimeout(Duration.ofMillis(5000)) // 设置读取超时时间 + .connectTimeout(Duration.ofMillis(5000)) // 设置连接超时时间 + .readTimeout(Duration.ofMillis(5000)) // 设置读取超时时间 .build(); } @@ -44,7 +47,13 @@ public class YudaoDeviceDataApiAutoConfiguration { */ @Bean public IotDeviceUpstreamApi deviceDataApi(RestTemplate restTemplate) { - return new DeviceDataApiClient(restTemplate, deviceDataUrl); + return new IotDeviceUpstreamClient(restTemplate, deviceDataUrl); + } + + @Bean(initMethod = "start", destroyMethod = "stop") + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") + public IotDeviceDownstreamServer deviceDownstreamServer(IotDeviceDownstreamHandler deviceDownstreamHandler) { + return new IotDeviceDownstreamServer(deviceDownstreamHandler); } } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/downstream/IotDeviceDownstreamHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/downstream/IotDeviceDownstreamHandler.java new file mode 100644 index 0000000..c140c32 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/downstream/IotDeviceDownstreamHandler.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.iot.plugin.common.core.downstream; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertyGetReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertySetReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceServiceInvokeReqDTO; + +/** + * IoT 设备下行处理器 + * + * 目的:每个 plugin 需要实现,用于处理 server 下行的指令(请求),从而实现从 server => plugin => device 的下行流程 + * + * @author 芋道源码 + */ +public interface IotDeviceDownstreamHandler { + + CommonResult invokeDeviceService(IotDeviceServiceInvokeReqDTO invokeReqDTO); + + CommonResult getDeviceProperty(IotDevicePropertyGetReqDTO getReqDTO); + + CommonResult setDeviceProperty(IotDevicePropertySetReqDTO setReqDTO); + +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/downstream/IotDeviceDownstreamServer.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/downstream/IotDeviceDownstreamServer.java new file mode 100644 index 0000000..310415f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/downstream/IotDeviceDownstreamServer.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.iot.plugin.common.core.downstream; + +import cn.iocoder.yudao.module.iot.plugin.common.core.downstream.router.IotDeviceServiceInvokeVertxHandler; +import io.vertx.core.Vertx; +import io.vertx.core.http.HttpServer; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.handler.BodyHandler; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class IotDeviceDownstreamServer { + + private final Vertx vertx; + private final HttpServer server; + + public IotDeviceDownstreamServer(IotDeviceDownstreamHandler deviceDownstreamHandler) { + // 创建 Vertx 实例 + this.vertx = Vertx.vertx(); + // 创建 Router 实例 + Router router = Router.router(vertx); + router.route().handler(BodyHandler.create()); // 处理 Body + router.post(IotDeviceServiceInvokeVertxHandler.PATH).handler( + new IotDeviceServiceInvokeVertxHandler(deviceDownstreamHandler)); // 处理 Service Invoke + // 创建 HttpServer 实例 + this.server = vertx.createHttpServer().requestHandler(router); + } + + public void start() { + log.info("[start][开始启动]"); + server.listen(0) // 通过 0 自动选择端口 + .toCompletionStage() + .toCompletableFuture() + .join(); + log.info("[start][启动完成,端口({})]", this.server.actualPort()); + } + + public void stop() { + log.info("[stop][开始关闭]"); + try { + // 关闭 HTTP 服务器 + if (server != null) { + server.close() + .toCompletionStage() + .toCompletableFuture() + .join(); + } + + // 关闭 Vertx 实例 + if (vertx != null) { + vertx.close() + .toCompletionStage() + .toCompletableFuture() + .join(); + } + log.info("[stop][关闭完成]"); + } catch (Exception e) { + log.error("[stop][关闭异常]", e); + throw new RuntimeException(e); + } + } + + /** + * 获得端口 + * + * @return 端口 + */ + public int getPort() { + return this.server.actualPort(); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/downstream/router/IotDeviceServiceInvokeVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/downstream/router/IotDeviceServiceInvokeVertxHandler.java new file mode 100644 index 0000000..2f53b62 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/downstream/router/IotDeviceServiceInvokeVertxHandler.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.iot.plugin.common.core.downstream.router; + +import cn.iocoder.yudao.module.iot.plugin.common.core.downstream.IotDeviceDownstreamHandler; +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@RequiredArgsConstructor +public class IotDeviceServiceInvokeVertxHandler implements Handler { + + public static final String PATH = "/sys/:productKey/:deviceName/thing/service/:identifier"; + + private final IotDeviceDownstreamHandler deviceDownstreamHandler; + + @Override + public void handle(RoutingContext routingContext) { + // TODO 芋艿:这里没实现 + deviceDownstreamHandler.invokeDeviceService(null); + } + +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/upstream/IotDeviceUpstreamClient.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/upstream/IotDeviceUpstreamClient.java new file mode 100644 index 0000000..5a729c0 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-common/src/main/java/cn/iocoder/yudao/module/iot/plugin/common/core/upstream/IotDeviceUpstreamClient.java @@ -0,0 +1,71 @@ +package cn.iocoder.yudao.module.iot.plugin.common.core.upstream; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceEventReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.client.RestTemplate; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +/** + * 设备数据 Upstream 上行客户端 + * + * 通过 HTTP 调用远程的 IotDeviceUpstreamApi 接口 + * + * @author haohao + */ +@Slf4j +public class IotDeviceUpstreamClient implements IotDeviceUpstreamApi { + + public static final String URL_PREFIX = "/rpc-api/iot/device/upstream"; + + private final RestTemplate restTemplate; + private final String deviceDataUrl; + + // 可以通过构造器把 RestTemplate 和 baseUrl 注入进来 + // TODO @haohao:可以用 lombok 简化 + public IotDeviceUpstreamClient(RestTemplate restTemplate, String deviceDataUrl) { + this.restTemplate = restTemplate; + this.deviceDataUrl = deviceDataUrl; + } + + // TODO @haohao:返回结果,不用 CommonResult 哈。 + @Override + public CommonResult updateDeviceState(IotDeviceStateUpdateReqDTO updateReqDTO) { + String url = deviceDataUrl + URL_PREFIX + "/update-state"; + return doPost(url, updateReqDTO, "updateDeviceState"); + } + + @Override + public CommonResult reportDeviceEvent(IotDeviceEventReportReqDTO reportReqDTO) { + String url = deviceDataUrl + URL_PREFIX + "/report-event"; + return doPost(url, reportReqDTO, "reportDeviceEventData"); + } + + @Override + public CommonResult reportDeviceProperty(IotDevicePropertyReportReqDTO reportReqDTO) { + String url = deviceDataUrl + URL_PREFIX + "/report-property"; + return doPost(url, reportReqDTO, "reportDevicePropertyData"); + } + + // TODO @haohao:未来可能有 get 类型哈 + /** + * 将与远程服务交互的通用逻辑抽取成一个私有方法 + */ + private CommonResult doPost(String url, T requestBody, String actionName) { + log.info("[{}] Sending request to URL: {}", actionName, url); + try { + // 这里指定返回类型为 CommonResult,根据后台服务返回的实际结构做调整 + restTemplate.postForObject(url, requestBody, CommonResult.class); + // TODO @haohao:check 结果,是否成功 + return success(true); + } catch (Exception e) { + log.error("[{}] Error sending request to URL: {}", actionName, url, e); + return CommonResult.error(400, "Request error: " + e.getMessage()); + } + } + +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/pom.xml b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/pom.xml index 15adef3..0bfa107 100644 --- a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/pom.xml +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/pom.xml @@ -121,12 +121,20 @@ + cn.iocoder.boot yudao-module-iot-plugin-common ${revision} + + + + org.springframework.boot + spring-boot-starter-web + + io.vertx diff --git a/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 b/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 index af54d1f..88d3765 100644 --- a/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 +++ b/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,7 +1,7 @@ package cn.iocoder.yudao.module.iot.plugin.http.config; import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; -import cn.iocoder.yudao.module.iot.plugin.http.service.HttpVertxHandler; +import cn.iocoder.yudao.module.iot.plugin.http.framework.upstream.HttpVertxHandler; import io.vertx.core.Vertx; import io.vertx.ext.web.Router; import io.vertx.ext.web.handler.BodyHandler; @@ -34,7 +34,7 @@ public class HttpVertxPluginConfiguration { /** * 创建路由 - * + * * @param vertx Vertx 实例 * @param httpVertxHandler HttpVertxHandler 实例 * @return Router 实例 @@ -68,7 +68,7 @@ public class HttpVertxPluginConfiguration { /** * 定义一个 VertxService 来管理服务器启动逻辑 * 无论是独立运行还是插件方式,都可以共用此类 - * + * * @param vertx Vertx 实例 * @param router Router 实例 * @return VertxService 实例 diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/framework/downstream/IotDeviceDownstreamHandlerImpl.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/framework/downstream/IotDeviceDownstreamHandlerImpl.java new file mode 100644 index 0000000..f01a1cb --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/framework/downstream/IotDeviceDownstreamHandlerImpl.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.iot.plugin.http.framework.downstream; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertyGetReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDevicePropertySetReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.downstream.IotDeviceServiceInvokeReqDTO; +import cn.iocoder.yudao.module.iot.plugin.common.core.downstream.IotDeviceDownstreamHandler; +import org.springframework.stereotype.Component; + +@Component // TODO @芋艿:后续统一处理 +public class IotDeviceDownstreamHandlerImpl implements IotDeviceDownstreamHandler { + + @Override + public CommonResult invokeDeviceService(IotDeviceServiceInvokeReqDTO invokeReqDTO) { + // TODO @芋艿:待实现 + System.out.println(); + return null; + } + + @Override + public CommonResult getDeviceProperty(IotDevicePropertyGetReqDTO getReqDTO) { + // TODO @芋艿:待实现 + System.out.println(); + return null; + } + + @Override + public CommonResult setDeviceProperty(IotDevicePropertySetReqDTO setReqDTO) { + // TODO @芋艿:待实现 + System.out.println(); + return null; + } + +} diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/framework/package-info.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/framework/package-info.java new file mode 100644 index 0000000..9f84cda --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/framework/package-info.java @@ -0,0 +1 @@ +package cn.iocoder.yudao.module.iot.plugin.http.framework; \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/framework/upstream/HttpVertxHandler.java b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/framework/upstream/HttpVertxHandler.java new file mode 100644 index 0000000..ec5976d --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-plugins/yudao-module-iot-plugin-http/src/main/java/cn/iocoder/yudao/module/iot/plugin/http/framework/upstream/HttpVertxHandler.java @@ -0,0 +1,93 @@ +package cn.iocoder.yudao.module.iot.plugin.http.framework.upstream; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDevicePropertyReportReqDTO; +import cn.iocoder.yudao.module.iot.api.device.dto.control.upstream.IotDeviceStateUpdateReqDTO; +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; +import io.vertx.core.Handler; +import io.vertx.ext.web.RequestBody; +import io.vertx.ext.web.RoutingContext; +import lombok.extern.slf4j.Slf4j; + +import java.time.LocalDateTime; +import java.util.Map; + +@Slf4j +public class HttpVertxHandler implements Handler { + + private final IotDeviceUpstreamApi deviceDataApi; + + public HttpVertxHandler(IotDeviceUpstreamApi deviceDataApi) { + this.deviceDataApi = deviceDataApi; + } + + @Override + public void handle(RoutingContext ctx) { + String productKey = ctx.pathParam("productKey"); + String deviceName = ctx.pathParam("deviceName"); + + // TODO @haohao:requestBody.asJsonObject() 貌似天然就是 json 对象哈? + RequestBody requestBody = ctx.body(); + JSONObject jsonData; + try { + jsonData = JSONUtil.parseObj(requestBody.asJsonObject()); + } catch (Exception e) { + log.error("[HttpVertxHandler] 请求数据解析失败", e); + ctx.response().setStatusCode(400) + .putHeader("Content-Type", "application/json; charset=UTF-8") + .end(createResponseJson(400, null, null, + "请求数据不是合法的 JSON 格式: " + e.getMessage(), + "thing.event.property.post", "1.0").toString()); + return; + } + + String id = jsonData.getStr("id"); + + try { + // TODO @haohao:pluginKey 需要设置 + // 设备上线 + deviceDataApi.updateDeviceState(((IotDeviceStateUpdateReqDTO) + new IotDeviceStateUpdateReqDTO().setRequestId(IdUtil.fastSimpleUUID()) + .setPluginKey("http") + .setReportTime(LocalDateTime.now()) + .setProductKey(productKey).setDeviceName(deviceName)) + .setState(IotDeviceStateEnum.ONLINE.getState())); + + // 属性上报 + deviceDataApi.reportDeviceProperty(((IotDevicePropertyReportReqDTO) + new IotDevicePropertyReportReqDTO().setRequestId(IdUtil.fastSimpleUUID()) + .setPluginKey("http").setReportTime(LocalDateTime.now()) + .setProductKey(productKey).setDeviceName(deviceName)) + .setProperties((Map) requestBody.asJsonObject().getMap().get("properties"))); + + ctx.response() + .setStatusCode(200) + .putHeader("Content-Type", "application/json; charset=UTF-8") + .end(createResponseJson(200, new JSONObject(), id, "success", + "thing.event.property.post", "1.0").toString()); + } catch (Exception e) { + log.error("[HttpVertxHandler] 上报属性数据失败", e); + ctx.response() + .setStatusCode(500) + .putHeader("Content-Type", "application/json; charset=UTF-8") + .end(createResponseJson(500, new JSONObject(), id, + "The format of result is error!", + "thing.event.property.post", "1.0").toString()); + } + } + + 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()); + res.set("id", id); + res.set("message", message); + res.set("method", method); + res.set("version", version); + return res; + } +} diff --git a/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 b/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 deleted file mode 100644 index 1f04e78..0000000 --- a/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 +++ /dev/null @@ -1,93 +0,0 @@ -package cn.iocoder.yudao.module.iot.plugin.http.service; - -import cn.hutool.core.util.IdUtil; -import cn.hutool.json.JSONObject; -import cn.hutool.json.JSONUtil; -import cn.iocoder.yudao.module.iot.api.device.IotDeviceUpstreamApi; -import cn.iocoder.yudao.module.iot.api.device.dto.IotDevicePropertyReportReqDTO; -import cn.iocoder.yudao.module.iot.api.device.dto.IotDeviceStateUpdateReqDTO; -import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum; -import io.vertx.core.Handler; -import io.vertx.ext.web.RequestBody; -import io.vertx.ext.web.RoutingContext; -import lombok.extern.slf4j.Slf4j; - -import java.time.LocalDateTime; -import java.util.Map; - -@Slf4j -public class HttpVertxHandler implements Handler { - - private final IotDeviceUpstreamApi deviceDataApi; - - public HttpVertxHandler(IotDeviceUpstreamApi deviceDataApi) { - this.deviceDataApi = deviceDataApi; - } - - @Override - public void handle(RoutingContext ctx) { - String productKey = ctx.pathParam("productKey"); - String deviceName = ctx.pathParam("deviceName"); - - // TODO @haohao:requestBody.asJsonObject() 貌似天然就是 json 对象哈? - RequestBody requestBody = ctx.body(); - JSONObject jsonData; - try { - jsonData = JSONUtil.parseObj(requestBody.asJsonObject()); - } catch (Exception e) { - log.error("[HttpVertxHandler] 请求数据解析失败", e); - ctx.response().setStatusCode(400) - .putHeader("Content-Type", "application/json; charset=UTF-8") - .end(createResponseJson(400, null, null, - "请求数据不是合法的 JSON 格式: " + e.getMessage(), - "thing.event.property.post", "1.0").toString()); - return; - } - - String id = jsonData.getStr("id"); - - try { - // TODO @haohao:pluginKey 需要设置 - // 设备上线 - deviceDataApi.updateDeviceState(((IotDeviceStateUpdateReqDTO) - new IotDeviceStateUpdateReqDTO().setRequestId(IdUtil.fastSimpleUUID()) - .setPluginKey("http") - .setReportTime(LocalDateTime.now()) - .setProductKey(productKey).setDeviceName(deviceName)) - .setState(IotDeviceStateEnum.ONLINE.getState())); - - // 属性上报 - deviceDataApi.reportDeviceProperty(((IotDevicePropertyReportReqDTO) - new IotDevicePropertyReportReqDTO().setRequestId(IdUtil.fastSimpleUUID()) - .setPluginKey("http").setReportTime(LocalDateTime.now()) - .setProductKey(productKey).setDeviceName(deviceName)) - .setProperties((Map) requestBody.asJsonObject().getMap().get("properties"))); - - ctx.response() - .setStatusCode(200) - .putHeader("Content-Type", "application/json; charset=UTF-8") - .end(createResponseJson(200, new JSONObject(), id, "success", - "thing.event.property.post", "1.0").toString()); - } catch (Exception e) { - log.error("[HttpVertxHandler] 上报属性数据失败", e); - ctx.response() - .setStatusCode(500) - .putHeader("Content-Type", "application/json; charset=UTF-8") - .end(createResponseJson(500, new JSONObject(), id, - "The format of result is error!", - "thing.event.property.post", "1.0").toString()); - } - } - - 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()); - res.set("id", id); - res.set("message", message); - res.set("method", method); - res.set("version", version); - return res; - } -} -- libgit2 0.21.4