getUpgradeTask(@RequestParam("id") Long id) {
+ IotOtaUpgradeTaskDO upgradeTask = upgradeTaskService.getUpgradeTask(id);
+ return success(BeanUtils.toBean(upgradeTask, IotOtaUpgradeTaskRespVO.class));
+ }
+
+}
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java
new file mode 100644
index 0000000000..50c2ece155
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareCreateReqVO.java
@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
+
+@Schema(description = "管理后台 - IoT OTA 固件创建 Request VO")
+@Data
+public class IotOtaFirmwareCreateReqVO {
+
+ @Schema(description = "固件名称", requiredMode = REQUIRED, example = "智能开关固件")
+ @NotEmpty(message = "固件名称不能为空")
+ private String name;
+
+ @Schema(description = "固件描述", example = "某品牌型号固件,测试用")
+ private String description;
+
+ @Schema(description = "版本号", requiredMode = REQUIRED, example = "1.0.0")
+ @NotEmpty(message = "版本号不能为空")
+ private String version;
+
+ @Schema(description = "产品编号", requiredMode = REQUIRED, example = "1024")
+ @NotNull(message = "产品编号不能为空")
+ private String productId;
+
+ @Schema(description = "签名方式", example = "MD5")
+ // TODO @li:是不是必传哈
+ private String signMethod;
+
+ @Schema(description = "固件文件 URL", requiredMode = REQUIRED, example = "https://www.iocoder.cn/yudao-firmware.zip")
+ @NotEmpty(message = "固件文件 URL 不能为空")
+ private String fileUrl;
+
+ @Schema(description = "自定义信息,建议使用 JSON 格式", example = "{\"key1\":\"value1\",\"key2\":\"value2\"}")
+ private String information;
+
+}
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java
new file mode 100644
index 0000000000..baa7410298
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwarePageReqVO.java
@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@Schema(description = "管理后台 - IoT OTA 固件分页 Request VO")
+public class IotOtaFirmwarePageReqVO extends PageParam {
+
+ /**
+ * 固件名称
+ */
+ @Schema(description = "固件名称", example = "智能开关固件")
+ private String name;
+
+ /**
+ * 产品标识
+ */
+ @Schema(description = "产品标识", example = "1024")
+ private String productId;
+
+}
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java
new file mode 100644
index 0000000000..735618781a
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareRespVO.java
@@ -0,0 +1,85 @@
+package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware;
+
+import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
+import com.fhs.core.trans.anno.Trans;
+import com.fhs.core.trans.constant.TransType;
+import com.fhs.core.trans.vo.VO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
+
+@Data
+@Schema(description = "管理后台 - IoT OTA 固件 Response VO")
+public class IotOtaFirmwareRespVO implements VO {
+
+ /**
+ * 固件编号
+ */
+ @Schema(description = "固件编号", requiredMode = REQUIRED, example = "1024")
+ private Long id;
+ /**
+ * 固件名称
+ */
+ @Schema(description = "固件名称", requiredMode = REQUIRED, example = "OTA固件")
+ private String name;
+ /**
+ * 固件描述
+ */
+ @Schema(description = "固件描述")
+ private String description;
+ /**
+ * 版本号
+ */
+ @Schema(description = "版本号", requiredMode = REQUIRED, example = "1.0.0")
+ private String version;
+
+ /**
+ * 产品编号
+ *
+ * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getId()}
+ */
+ @Schema(description = "产品编号", requiredMode = REQUIRED, example = "1024")
+ @Trans(type = TransType.SIMPLE, target = IotProductDO.class, fields = {"name"}, refs = {"productName"})
+ private String productId;
+ /**
+ * 产品标识
+ *
+ * 冗余 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getProductKey()}
+ */
+ @Schema(description = "产品标识", requiredMode = REQUIRED, example = "iot-product-key")
+ private String productKey;
+ /**
+ * 产品名称
+ */
+ @Schema(description = "产品名称", requiredMode = REQUIRED, example = "OTA产品")
+ private String productName;
+ /**
+ * 签名方式
+ *
+ * 例如说:MD5、SHA256
+ */
+ @Schema(description = "签名方式", example = "MD5")
+ private String signMethod;
+ /**
+ * 固件文件签名
+ */
+ @Schema(description = "固件文件签名", example = "1024")
+ private String fileSign;
+ /**
+ * 固件文件大小
+ */
+ @Schema(description = "固件文件大小", requiredMode = REQUIRED, example = "1024")
+ private Long fileSize;
+ /**
+ * 固件文件 URL
+ */
+ @Schema(description = "固件文件 URL", requiredMode = REQUIRED, example = "https://www.iocoder.cn")
+ private String fileUrl;
+ /**
+ * 自定义信息,建议使用 JSON 格式
+ */
+ @Schema(description = "自定义信息,建议使用 JSON 格式")
+ private String information;
+
+}
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java
new file mode 100644
index 0000000000..aa134bceef
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/firmware/IotOtaFirmwareUpdateReqVO.java
@@ -0,0 +1,26 @@
+package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.firmware;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
+
+@Schema(description = "管理后台 - IoT OTA 固件更新 Request VO")
+@Data
+public class IotOtaFirmwareUpdateReqVO {
+
+ @Schema(description = "固件编号", requiredMode = REQUIRED, example = "1024")
+ @NotNull(message = "固件编号不能为空")
+ private Long id;
+
+ // TODO @li:name 是不是可以飞必传哈
+ @Schema(description = "固件名称", requiredMode = REQUIRED, example = "智能开关固件")
+ @NotEmpty(message = "固件名称不能为空")
+ private String name;
+
+ @Schema(description = "固件描述", example = "某品牌型号固件,测试用")
+ private String description;
+
+}
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java
new file mode 100644
index 0000000000..2b21b30796
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordPageReqVO.java
@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
+
+@Data
+@Schema(description = "管理后台 - IoT OTA 升级记录分页 Request VO")
+public class IotOtaUpgradeRecordPageReqVO extends PageParam {
+
+ // TODO @li:已经有注解,不用重复注释
+ /**
+ * 升级任务编号字段。
+ *
+ * 该字段用于标识升级任务的唯一编号,不能为空。
+ */
+ @Schema(description = "升级任务编号", requiredMode = REQUIRED, example = "1024")
+ @NotNull(message = "升级任务编号不能为空")
+ private Long taskId;
+
+ /**
+ * 设备标识字段。
+ *
+ * 该字段用于标识设备的名称,通常用于区分不同的设备。
+ */
+ @Schema(description = "设备标识", requiredMode = REQUIRED, example = "摄像头A1-1")
+ private String deviceName;
+
+}
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordRespVO.java
new file mode 100644
index 0000000000..db6737febb
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/record/IotOtaUpgradeRecordRespVO.java
@@ -0,0 +1,109 @@
+package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.record;
+
+import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaUpgradeTaskDO;
+import com.fhs.core.trans.anno.Trans;
+import com.fhs.core.trans.constant.TransType;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
+
+@Data
+@Schema(description = "管理后台 - IoT OTA 升级记录 Response VO")
+public class IotOtaUpgradeRecordRespVO {
+
+ /**
+ * 升级记录编号
+ */
+ @Schema(description = "升级记录编号", requiredMode = REQUIRED, example = "1024")
+ private Long id;
+ /**
+ * 固件编号
+ *
+ * 关联 {@link IotOtaFirmwareDO#getId()}
+ */
+ @Schema(description = "固件编号", requiredMode = REQUIRED, example = "1024")
+ @Trans(type = TransType.SIMPLE, target = IotOtaFirmwareDO.class, fields = {"version"}, refs = {"firmwareVersion"})
+ private Long firmwareId;
+ /**
+ * 固件版本
+ */
+ @Schema(description = "固件版本", requiredMode = REQUIRED, example = "v1.0.0")
+ private String firmwareVersion;
+ /**
+ * 任务编号
+ *
+ * 关联 {@link IotOtaUpgradeTaskDO#getId()}
+ */
+ @Schema(description = "任务编号", requiredMode = REQUIRED, example = "1024")
+ private Long taskId;
+ /**
+ * 产品标识
+ *
+ * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO#getId()}
+ */
+ @Schema(description = "产品标识", requiredMode = REQUIRED, example = "iot")
+ private String productKey;
+ /**
+ * 设备名称
+ *
+ * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO#getId()}
+ */
+ @Schema(description = "设备名称", requiredMode = REQUIRED, example = "iot")
+ private String deviceName;
+ /**
+ * 设备编号
+ *
+ * 关联 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO#getId()}
+ */
+ @Schema(description = "设备编号", requiredMode = REQUIRED, example = "1024")
+ private String deviceId;
+ /**
+ * 来源的固件编号
+ *
+ * 关联 {@link IotDeviceDO#getFirmwareId()}
+ */
+ @Schema(description = "来源的固件编号", requiredMode = REQUIRED, example = "1024")
+ @Trans(type = TransType.SIMPLE, target = IotOtaFirmwareDO.class, fields = {"version"}, refs = {"fromFirmwareVersion"})
+ private Long fromFirmwareId;
+ /**
+ * 来源的固件版本
+ */
+ @Schema(description = "来源的固件版本", requiredMode = REQUIRED, example = "v1.0.0")
+ private String fromFirmwareVersion;
+ /**
+ * 升级状态
+ *
+ * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeRecordStatusEnum}
+ */
+ @Schema(description = "升级状态", requiredMode = REQUIRED, allowableValues = {"0", "10", "20", "30", "40", "50"})
+ private Integer status;
+ /**
+ * 升级进度,百分比
+ */
+ @Schema(description = "升级进度,百分比", requiredMode = REQUIRED, example = "10")
+ private Integer progress;
+ /**
+ * 升级进度描述
+ *
+ * 注意,只记录设备最后一次的升级进度描述
+ * 如果想看历史记录,可以查看 {@link cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceLogDO} 设备日志
+ */
+ @Schema(description = "升级进度描述", requiredMode = REQUIRED, example = "10")
+ private String description;
+ /**
+ * 升级开始时间
+ */
+ @Schema(description = "升级开始时间", requiredMode = REQUIRED, example = "2022-07-08 07:30:00")
+ private LocalDateTime startTime;
+ /**
+ * 升级结束时间
+ */
+ @Schema(description = "升级结束时间", requiredMode = REQUIRED, example = "2022-07-08 07:30:00")
+ private LocalDateTime endTime;
+
+}
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskPageReqVO.java
new file mode 100644
index 0000000000..d2b1926aa6
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskPageReqVO.java
@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
+
+@Data
+@Schema(description = "管理后台 - IoT OTA 升级任务分页 Request VO")
+public class IotOtaUpgradeTaskPageReqVO extends PageParam {
+
+ /**
+ * 任务名称字段,用于描述任务的名称
+ */
+ @Schema(description = "任务名称", example = "升级任务")
+ private String name;
+
+ /**
+ * 固件编号字段,用于唯一标识固件,不能为空
+ */
+ @NotNull(message = "固件编号不能为空")
+ @Schema(description = "固件编号", requiredMode = REQUIRED, example = "1024")
+ private Long firmwareId;
+
+}
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskRespVO.java
new file mode 100644
index 0000000000..dbc29618f8
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskRespVO.java
@@ -0,0 +1,84 @@
+package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task;
+
+import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;
+import com.fhs.core.trans.vo.VO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
+
+@Data
+@Schema(description = "管理后台 - IoT OTA 升级任务 Response VO")
+public class IotOtaUpgradeTaskRespVO implements VO {
+
+ /**
+ * 任务编号
+ */
+ @Schema(description = "任务编号", requiredMode = REQUIRED, example = "1024")
+ private Long id;
+ /**
+ * 任务名称
+ */
+ @Schema(description = "任务名称", requiredMode = REQUIRED, example = "升级任务")
+ private String name;
+ /**
+ * 任务描述
+ */
+ @Schema(description = "任务描述", example = "升级任务")
+ private String description;
+ /**
+ * 固件编号
+ *
+ * 关联 {@link IotOtaFirmwareDO#getId()}
+ */
+ @Schema(description = "固件编号", requiredMode = REQUIRED, example = "1024")
+ private Long firmwareId;
+ /**
+ * 任务状态
+ *
+ * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskStatusEnum}
+ */
+ @Schema(description = "任务状态", requiredMode = REQUIRED, allowableValues = {"10", "20", "21", "30"})
+ private Integer status;
+ /**
+ * 任务状态名称
+ */
+ @Schema(description = "任务状态名称", requiredMode = REQUIRED, example = "进行中")
+ private String statusName;
+ /**
+ * 升级范围
+ *
+ * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskScopeEnum}
+ */
+ @Schema(description = "升级范围", requiredMode = REQUIRED, allowableValues = {"1", "2"})
+ private Integer scope;
+ /**
+ * 设备数量
+ */
+ @Schema(description = "设备数量", requiredMode = REQUIRED, example = "1024")
+ private Long deviceCount;
+ /**
+ * 选中的设备编号数组
+ *
+ * 关联 {@link IotDeviceDO#getId()}
+ */
+ @Schema(description = "选中的设备编号数组", example = "1024")
+ private List deviceIds;
+ /**
+ * 选中的设备名字数组
+ *
+ * 关联 {@link IotDeviceDO#getDeviceName()}
+ */
+ @Schema(description = "选中的设备名字数组", example = "1024")
+ private List deviceNames;
+ /**
+ * 创建时间
+ */
+ @Schema(description = "创建时间", requiredMode = REQUIRED, example = "2022-07-08 07:30:00")
+ private LocalDateTime createTime;
+
+}
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java
new file mode 100644
index 0000000000..0ace17a047
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/ota/vo/upgrade/task/IotOtaUpgradeTaskSaveReqVO.java
@@ -0,0 +1,63 @@
+package cn.iocoder.yudao.module.iot.controller.admin.ota.vo.upgrade.task;
+
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.ota.IotOtaFirmwareDO;
+import cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskScopeEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.util.List;
+
+import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
+
+@Data
+@Schema(description = "管理后台 - IoT OTA 升级任务创建/修改 Request VO")
+public class IotOtaUpgradeTaskSaveReqVO {
+
+ // TODO @li:已经有注解,不用重复注释
+ // TODO @li: @Schema 写在参数校验前面。先有定义;其他的,也检查下;
+
+ /**
+ * 任务名称
+ */
+ @NotEmpty(message = "任务名称不能为空")
+ @Schema(description = "任务名称", requiredMode = REQUIRED, example = "升级任务")
+ private String name;
+
+ /**
+ * 任务描述
+ */
+ @Schema(description = "任务描述", example = "升级任务")
+ private String description;
+
+ /**
+ * 固件编号
+ *
+ * 关联 {@link IotOtaFirmwareDO#getId()}
+ */
+ @NotNull(message = "固件编号不能为空")
+ @Schema(description = "固件编号", requiredMode = REQUIRED, example = "1024")
+ private Long firmwareId;
+
+ /**
+ * 升级范围
+ *
+ * 关联 {@link cn.iocoder.yudao.module.iot.enums.ota.IotOtaUpgradeTaskScopeEnum}
+ */
+ @NotNull(message = "升级范围不能为空")
+ @InEnum(value = IotOtaUpgradeTaskScopeEnum.class)
+ @Schema(description = "升级范围", requiredMode = REQUIRED, example = "1")
+ private Integer scope;
+
+ /**
+ * 选中的设备编号数组
+ *
+ * 关联 {@link IotDeviceDO#getId()}
+ */
+ @Schema(description = "选中的设备编号数组", requiredMode = REQUIRED, example = "[1,2,3,4]")
+ private List deviceIds;
+
+}
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginConfigController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginConfigController.java
new file mode 100644
index 0000000000..e21b102410
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginConfigController.java
@@ -0,0 +1,90 @@
+package cn.iocoder.yudao.module.iot.controller.admin.plugin;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config.PluginConfigImportReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config.PluginConfigPageReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config.PluginConfigRespVO;
+import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config.PluginConfigSaveReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config.PluginConfigStatusReqVO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginConfigDO;
+import cn.iocoder.yudao.module.iot.service.plugin.IotPluginConfigService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - IoT 插件配置")
+@RestController
+@RequestMapping("/iot/plugin-config")
+@Validated
+public class PluginConfigController {
+
+ @Resource
+ private IotPluginConfigService pluginConfigService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建插件配置")
+ @PreAuthorize("@ss.hasPermission('iot:plugin-config:create')")
+ public CommonResult createPluginConfig(@Valid @RequestBody PluginConfigSaveReqVO createReqVO) {
+ return success(pluginConfigService.createPluginConfig(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新插件配置")
+ @PreAuthorize("@ss.hasPermission('iot:plugin-config:update')")
+ public CommonResult updatePluginConfig(@Valid @RequestBody PluginConfigSaveReqVO updateReqVO) {
+ pluginConfigService.updatePluginConfig(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除插件配置")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('iot:plugin-config:delete')")
+ public CommonResult deletePluginConfig(@RequestParam("id") Long id) {
+ pluginConfigService.deletePluginConfig(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得插件配置")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('iot:plugin-config:query')")
+ public CommonResult getPluginConfig(@RequestParam("id") Long id) {
+ IotPluginConfigDO pluginConfig = pluginConfigService.getPluginConfig(id);
+ return success(BeanUtils.toBean(pluginConfig, PluginConfigRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得插件配置分页")
+ @PreAuthorize("@ss.hasPermission('iot:plugin-config:query')")
+ public CommonResult> getPluginConfigPage(@Valid PluginConfigPageReqVO pageReqVO) {
+ PageResult pageResult = pluginConfigService.getPluginConfigPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, PluginConfigRespVO.class));
+ }
+
+ @PostMapping("/upload-file")
+ @Operation(summary = "上传插件文件")
+ @PreAuthorize("@ss.hasPermission('iot:plugin-config:update')")
+ public CommonResult uploadFile(@Valid PluginConfigImportReqVO reqVO) {
+ pluginConfigService.uploadFile(reqVO.getId(), reqVO.getFile());
+ return success(true);
+ }
+
+ @PutMapping("/update-status")
+ @Operation(summary = "修改插件状态")
+ @PreAuthorize("@ss.hasPermission('iot:plugin-config:update')")
+ public CommonResult updatePluginConfigStatus(@Valid @RequestBody PluginConfigStatusReqVO reqVO) {
+ pluginConfigService.updatePluginStatus(reqVO.getId(), reqVO.getStatus());
+ return success(true);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigImportReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigImportReqVO.java
new file mode 100644
index 0000000000..b9b277a542
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigImportReqVO.java
@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import org.springframework.web.multipart.MultipartFile;
+
+@Schema(description = "管理后台 - IoT 插件上传 Request VO")
+@Data
+public class PluginConfigImportReqVO {
+
+ @Schema(description = "主键 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546")
+ private Long id;
+
+ @Schema(description = "插件文件", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "插件文件不能为空")
+ private MultipartFile file;
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigPageReqVO.java
new file mode 100644
index 0000000000..1666d5d6bc
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigPageReqVO.java
@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - IoT 插件配置分页 Request VO")
+@Data
+public class PluginConfigPageReqVO extends PageParam {
+
+ @Schema(description = "插件名称", example = "http")
+ private String name;
+
+ @Schema(description = "状态", example = "1")
+ @InEnum(IotPluginStatusEnum.class)
+ private Integer status;
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigRespVO.java
new file mode 100644
index 0000000000..2b8c4dcde8
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigRespVO.java
@@ -0,0 +1,54 @@
+package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - IoT 插件配置 Response VO")
+@Data
+public class PluginConfigRespVO {
+
+ @Schema(description = "主键 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546")
+ private Long id;
+
+ @Schema(description = "插件包标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627")
+ private String pluginKey;
+
+ @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
+ private String name;
+
+ @Schema(description = "描述", example = "你猜")
+ private String description;
+
+ @Schema(description = "部署方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+ private Integer deployType;
+
+ @Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED)
+ private String fileName;
+
+ @Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED)
+ private String version;
+
+ @Schema(description = "插件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+ private Integer type;
+
+ @Schema(description = "设备插件协议类型")
+ private String protocol;
+
+ @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED)
+ private Integer status;
+
+ @Schema(description = "插件配置项描述信息")
+ private String configSchema;
+
+ @Schema(description = "插件配置信息")
+ private String config;
+
+ @Schema(description = "插件脚本")
+ private String script;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigSaveReqVO.java
new file mode 100644
index 0000000000..e48869d645
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigSaveReqVO.java
@@ -0,0 +1,56 @@
+package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config;
+
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - IoT 插件配置新增/修改 Request VO")
+@Data
+public class PluginConfigSaveReqVO {
+
+ // TODO @haohao:新增的字段有点多,每个都需要哇?
+
+ // TODO @haohao:一些枚举字段,需要加枚举校验。例如说,deployType、status、type 等
+
+ @Schema(description = "主键编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546")
+ private Long id;
+
+ @Schema(description = "插件包标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627")
+ private String pluginKey;
+
+ @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
+ private String name;
+
+ @Schema(description = "描述", example = "你猜")
+ private String description;
+
+ @Schema(description = "部署方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+ private Integer deployType;
+
+ @Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED)
+ private String fileName;
+
+ @Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED)
+ private String version;
+
+ @Schema(description = "插件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+ private Integer type;
+
+ @Schema(description = "设备插件协议类型")
+ private String protocol;
+
+ @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED)
+ @InEnum(IotPluginStatusEnum.class)
+ private Integer status;
+
+ @Schema(description = "插件配置项描述信息")
+ private String configSchema;
+
+ @Schema(description = "插件配置信息")
+ private String config;
+
+ @Schema(description = "插件脚本")
+ private String script;
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigStatusReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigStatusReqVO.java
new file mode 100644
index 0000000000..eae4aa0a2e
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/config/PluginConfigStatusReqVO.java
@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.config;
+
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - IoT 插件配置状态 Request VO")
+@Data
+public class PluginConfigStatusReqVO {
+
+ @Schema(description = "主键编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546")
+ private Long id;
+
+ @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED)
+ @InEnum(IotPluginStatusEnum.class)
+ private Integer status;
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/instance/PluginInstancePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/instance/PluginInstancePageReqVO.java
new file mode 100644
index 0000000000..e58b88856e
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/instance/PluginInstancePageReqVO.java
@@ -0,0 +1,35 @@
+package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.instance;
+
+import lombok.*;
+import io.swagger.v3.oas.annotations.media.Schema;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import org.springframework.format.annotation.DateTimeFormat;
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+// TODO @haohao:后续需要使用下
+@Schema(description = "管理后台 - IoT 插件实例分页 Request VO")
+@Data
+public class PluginInstancePageReqVO extends PageParam {
+
+ @Schema(description = "插件主程序编号", example = "23738")
+ private String mainId;
+
+ @Schema(description = "插件id", example = "26498")
+ private Long pluginId;
+
+ @Schema(description = "插件主程序所在ip")
+ private String ip;
+
+ @Schema(description = "插件主程序端口")
+ private Integer port;
+
+ @Schema(description = "心跳时间,心路时间超过30秒需要剔除")
+ private Long heartbeatAt;
+
+ @Schema(description = "创建时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/instance/PluginInstanceRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/instance/PluginInstanceRespVO.java
new file mode 100644
index 0000000000..cba59fdaf5
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/instance/PluginInstanceRespVO.java
@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.instance;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+// TODO @haohao:后续需要使用下
+@Schema(description = "管理后台 - IoT 插件实例 Response VO")
+@Data
+public class PluginInstanceRespVO {
+
+ @Schema(description = "主键编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23864")
+ private Long id;
+
+ @Schema(description = "插件主程序id", requiredMode = Schema.RequiredMode.REQUIRED, example = "23738")
+ private String mainId;
+
+ @Schema(description = "插件id", requiredMode = Schema.RequiredMode.REQUIRED, example = "26498")
+ private Long pluginId;
+
+ @Schema(description = "插件主程序所在ip", requiredMode = Schema.RequiredMode.REQUIRED)
+ private String ip;
+
+ @Schema(description = "插件主程序端口", requiredMode = Schema.RequiredMode.REQUIRED)
+ private Integer port;
+
+ @Schema(description = "心跳时间,心路时间超过30秒需要剔除", requiredMode = Schema.RequiredMode.REQUIRED)
+ private Long heartbeatAt;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductCategoryController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductCategoryController.java
new file mode 100644
index 0000000000..bc1c1fbf2b
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductCategoryController.java
@@ -0,0 +1,86 @@
+package cn.iocoder.yudao.module.iot.controller.admin.product;
+
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryPageReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategoryRespVO;
+import cn.iocoder.yudao.module.iot.controller.admin.product.vo.category.IotProductCategorySaveReqVO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO;
+import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
+
+@Tag(name = "管理后台 - IoT 产品分类")
+@RestController
+@RequestMapping("/iot/product-category")
+@Validated
+public class IotProductCategoryController {
+
+ @Resource
+ private IotProductCategoryService productCategoryService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建产品分类")
+ @PreAuthorize("@ss.hasPermission('iot:product-category:create')")
+ public CommonResult createProductCategory(@Valid @RequestBody IotProductCategorySaveReqVO createReqVO) {
+ return success(productCategoryService.createProductCategory(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新产品分类")
+ @PreAuthorize("@ss.hasPermission('iot:product-category:update')")
+ public CommonResult updateProductCategory(@Valid @RequestBody IotProductCategorySaveReqVO updateReqVO) {
+ productCategoryService.updateProductCategory(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除产品分类")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('iot:product-category:delete')")
+ public CommonResult deleteProductCategory(@RequestParam("id") Long id) {
+ productCategoryService.deleteProductCategory(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得产品分类")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('iot:product-category:query')")
+ public CommonResult getProductCategory(@RequestParam("id") Long id) {
+ IotProductCategoryDO productCategory = productCategoryService.getProductCategory(id);
+ return success(BeanUtils.toBean(productCategory, IotProductCategoryRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得产品分类分页")
+ @PreAuthorize("@ss.hasPermission('iot:product-category:query')")
+ public CommonResult> getProductCategoryPage(@Valid IotProductCategoryPageReqVO pageReqVO) {
+ PageResult pageResult = productCategoryService.getProductCategoryPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, IotProductCategoryRespVO.class));
+ }
+
+ @GetMapping("/simple-list")
+ @Operation(summary = "获得所有产品分类列表")
+ @PreAuthorize("@ss.hasPermission('iot:product-category:query')")
+ public CommonResult> getSimpleProductCategoryList() {
+ List list = productCategoryService.getProductCategoryListByStatus(
+ CommonStatusEnum.ENABLE.getStatus());
+ return success(convertList(list, category ->
+ new IotProductCategoryRespVO().setId(category.getId()).setName(category.getName())));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java
index 5b0ecb27ae..2d8c856400 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java
@@ -1,26 +1,36 @@
package cn.iocoder.yudao.module.iot.controller.admin.product;
+import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.iot.controller.admin.product.vo.IotProductPageReqVO;
-import cn.iocoder.yudao.module.iot.controller.admin.product.vo.IotProductRespVO;
-import cn.iocoder.yudao.module.iot.controller.admin.product.vo.IotProductSaveReqVO;
-import cn.iocoder.yudao.module.iot.controller.admin.product.vo.IotProductSimpleRespVO;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductPageReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductRespVO;
+import cn.iocoder.yudao.module.iot.controller.admin.product.vo.product.IotProductSaveReqVO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductCategoryDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
+import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService;
import cn.iocoder.yudao.module.iot.service.product.IotProductService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
+import java.io.IOException;
import java.util.List;
+import java.util.Map;
+import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@Tag(name = "管理后台 - IoT 产品")
@RestController
@@ -30,6 +40,8 @@ public class IotProductController {
@Resource
private IotProductService productService;
+ @Resource
+ private IotProductCategoryService categoryService;
@PostMapping("/create")
@Operation(summary = "创建产品")
@@ -72,7 +84,13 @@ public class IotProductController {
@PreAuthorize("@ss.hasPermission('iot:product:query')")
public CommonResult getProduct(@RequestParam("id") Long id) {
IotProductDO product = productService.getProduct(id);
- return success(BeanUtils.toBean(product, IotProductRespVO.class));
+ // 拼接数据
+ IotProductCategoryDO category = categoryService.getProductCategory(product.getCategoryId());
+ return success(BeanUtils.toBean(product, IotProductRespVO.class, bean -> {
+ if (category != null) {
+ bean.setCategoryName(category.getName());
+ }
+ }));
}
@GetMapping("/page")
@@ -80,16 +98,35 @@ public class IotProductController {
@PreAuthorize("@ss.hasPermission('iot:product:query')")
public CommonResult> getProductPage(@Valid IotProductPageReqVO pageReqVO) {
PageResult pageResult = productService.getProductPage(pageReqVO);
- return success(BeanUtils.toBean(pageResult, IotProductRespVO.class));
+ // 拼接数据
+ Map categoryMap = categoryService.getProductCategoryMap(
+ convertList(pageResult.getList(), IotProductDO::getCategoryId));
+ return success(BeanUtils.toBean(pageResult, IotProductRespVO.class, bean -> {
+ MapUtils.findAndThen(categoryMap, bean.getCategoryId(),
+ category -> bean.setCategoryName(category.getName()));
+ }));
}
- // TODO @haohao:改成 simple-list 哈
- @GetMapping("/list-all-simple")
- @Operation(summary = "获得所有产品列表")
- @PreAuthorize("@ss.hasPermission('iot:product:query')")
- public CommonResult> listAllSimpleProducts() {
+ @GetMapping("/export-excel")
+ @Operation(summary = "导出产品 Excel")
+ @PreAuthorize("@ss.hasPermission('iot:product:export')")
+ @ApiAccessLog(operateType = EXPORT)
+ public void exportProductExcel(@Valid IotProductPageReqVO exportReqVO,
+ HttpServletResponse response) throws IOException {
+ exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+ CommonResult> result = getProductPage(exportReqVO);
+ // 导出 Excel
+ ExcelUtils.write(response, "产品.xls", "数据", IotProductRespVO.class,
+ result.getData().getList());
+ }
+
+ @GetMapping("/simple-list")
+ @Operation(summary = "获取产品的精简信息列表", description = "主要用于前端的下拉选项")
+ public CommonResult> getSimpleProductList() {
List list = productService.getProductList();
- return success(BeanUtils.toBean(list, IotProductSimpleRespVO.class));
+ return success(convertList(list, product -> // 只返回 id、name 字段
+ new IotProductRespVO().setId(product.getId()).setName(product.getName())
+ .setDeviceType(product.getDeviceType())));
}
}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductSimpleRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductSimpleRespVO.java
deleted file mode 100644
index 83855eaaf8..0000000000
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductSimpleRespVO.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package cn.iocoder.yudao.module.iot.controller.admin.product.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
-@Schema(description = "管理后台 - IoT 产品 Response VO")
-@Data
-public class IotProductSimpleRespVO {
-
- @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26087")
- private Long id;
-
- @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
- private String name;
-
-}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryPageReqVO.java
new file mode 100644
index 0000000000..f1c12bf7cb
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryPageReqVO.java
@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.module.iot.controller.admin.product.vo.category;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - IoT 产品分类分页 Request VO")
+@Data
+public class IotProductCategoryPageReqVO extends PageParam {
+
+ @Schema(description = "分类名字", example = "王五")
+ private String name;
+
+ @Schema(description = "创建时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryRespVO.java
new file mode 100644
index 0000000000..d684b0215a
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategoryRespVO.java
@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.iot.controller.admin.product.vo.category;
+
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.module.system.enums.DictTypeConstants;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - IoT 产品分类 Response VO")
+@Data
+public class IotProductCategoryRespVO {
+
+ @Schema(description = "分类 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25284")
+ private Long id;
+
+ @Schema(description = "分类名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
+ private String name;
+
+ @Schema(description = "分类排序")
+ private Integer sort;
+
+ @Schema(description = "分类状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @DictFormat(DictTypeConstants.COMMON_STATUS)
+ private Integer status;
+
+ @Schema(description = "分类描述", example = "随便")
+ private String description;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategorySaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategorySaveReqVO.java
new file mode 100644
index 0000000000..a7b2fe4271
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/category/IotProductCategorySaveReqVO.java
@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.iot.controller.admin.product.vo.category;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - IoT 产品分类新增/修改 Request VO")
+@Data
+public class IotProductCategorySaveReqVO {
+
+ @Schema(description = "分类 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "25284")
+ private Long id;
+
+ @Schema(description = "分类名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
+ @NotEmpty(message = "分类名字不能为空")
+ private String name;
+
+ @Schema(description = "分类排序")
+ private Integer sort;
+
+ @Schema(description = "分类状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "分类状态不能为空")
+ private Integer status;
+
+ @Schema(description = "分类描述", example = "随便")
+ private String description;
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductPageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductPageReqVO.java
similarity index 78%
rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductPageReqVO.java
rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductPageReqVO.java
index 3437f563fd..18c69c4cec 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductPageReqVO.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductPageReqVO.java
@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.iot.controller.admin.product.vo;
+package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
@@ -8,8 +8,6 @@ import lombok.ToString;
@Schema(description = "管理后台 - IoT 产品分页 Request VO")
@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
public class IotProductPageReqVO extends PageParam {
@Schema(description = "产品名称", example = "李四")
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java
similarity index 56%
rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductRespVO.java
rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java
index 0958b3e844..f674651d51 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductRespVO.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductRespVO.java
@@ -1,5 +1,8 @@
-package cn.iocoder.yudao.module.iot.controller.admin.product.vo;
+package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product;
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+import cn.iocoder.yudao.module.iot.enums.DictTypeConstants;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
@@ -20,48 +23,65 @@ public class IotProductRespVO {
@ExcelProperty("产品名称")
private String name;
- @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
- @ExcelProperty("创建时间")
- private LocalDateTime createTime;
-
@Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("产品标识")
private String productKey;
+ @Schema(description = "产品分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Long categoryId;
+
+ @Schema(description = "产品分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @ExcelProperty("产品分类")
+ private String categoryName;
+
+ @Schema(description = "产品图标", example = "https://iocoder.cn/1.svg")
+ @ExcelProperty("产品图标")
+ private String icon;
+
+ @Schema(description = "产品图片", example = "https://iocoder.cn/1.png")
+ @ExcelProperty("产品图片")
+ private String picUrl;
+
+ @Schema(description = "产品描述", example = "你猜")
+ @ExcelProperty("产品描述")
+ private String description;
+
+ @Schema(description = "产品状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @ExcelProperty(value = "产品状态", converter = DictConvert.class)
+ @DictFormat(DictTypeConstants.PRODUCT_STATUS)
+ private Integer status;
+
+ @Schema(description = "设备类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+ @ExcelProperty(value = "设备类型", converter = DictConvert.class)
+ @DictFormat(DictTypeConstants.PRODUCT_DEVICE_TYPE)
+ private Integer deviceType;
+
+ @Schema(description = "联网方式", example = "2")
+ @ExcelProperty(value = "联网方式", converter = DictConvert.class)
+ @DictFormat(DictTypeConstants.NET_TYPE)
+ private Integer netType;
+
@Schema(description = "接入网关协议", example = "2")
- @ExcelProperty("接入网关协议")
+ @ExcelProperty(value = "接入网关协议", converter = DictConvert.class)
+ @DictFormat(DictTypeConstants.PROTOCOL_TYPE)
private Integer protocolType;
@Schema(description = "协议编号(脚本解析 id)", requiredMode = Schema.RequiredMode.REQUIRED, example = "13177")
@ExcelProperty("协议编号(脚本解析 id)")
private Long protocolId;
- @Schema(description = "产品所属品类标识符", example = "14237")
- @ExcelProperty("产品所属品类标识符")
- private Long categoryId;
-
- @Schema(description = "产品描述", example = "你猜")
- @ExcelProperty("产品描述")
- private String description;
-
- @Schema(description = "数据校验级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @ExcelProperty("数据校验级别")
- private Integer validateType;
-
- @Schema(description = "产品状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @ExcelProperty("产品状态")
- private Integer status;
-
- @Schema(description = "设备类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
- @ExcelProperty("设备类型")
- private Integer deviceType;
-
- @Schema(description = "联网方式", example = "2")
- @ExcelProperty("联网方式")
- private Integer netType;
-
@Schema(description = "数据格式")
- @ExcelProperty("数据格式")
+ @ExcelProperty(value = "数据格式", converter = DictConvert.class)
+ @DictFormat(DictTypeConstants.DATA_FORMAT)
private Integer dataFormat;
+ @Schema(description = "数据校验级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @ExcelProperty(value = "数据校验级别", converter = DictConvert.class)
+ @DictFormat(DictTypeConstants.VALIDATE_TYPE)
+ private Integer validateType;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ @ExcelProperty("创建时间")
+ private LocalDateTime createTime;
+
}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java
similarity index 78%
rename from yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductSaveReqVO.java
rename to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java
index 254b6b9da2..268ab7c6fa 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/IotProductSaveReqVO.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/vo/product/IotProductSaveReqVO.java
@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.module.iot.controller.admin.product.vo;
+package cn.iocoder.yudao.module.iot.controller.admin.product.vo.product;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.iot.enums.product.*;
@@ -14,13 +14,26 @@ public class IotProductSaveReqVO {
@Schema(description = "产品编号", requiredMode = Schema.RequiredMode.AUTO, example = "1")
private Long id;
- @Schema(description = "产品Key", requiredMode = Schema.RequiredMode.AUTO, example = "12345abc")
- private String productKey;
-
@Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "温湿度")
@NotEmpty(message = "产品名称不能为空")
private String name;
+ @Schema(description = "产品 Key", requiredMode = Schema.RequiredMode.AUTO, example = "12345abc")
+ private String productKey;
+
+ @Schema(description = "产品分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "产品分类编号不能为空")
+ private Long categoryId;
+
+ @Schema(description = "产品图标", example = "https://iocoder.cn/1.svg")
+ private String icon;
+
+ @Schema(description = "产品图片", example = "https://iocoder.cn/1.png")
+ private String picUrl;
+
+ @Schema(description = "产品描述", example = "描述")
+ private String description;
+
@Schema(description = "设备类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
@InEnum(value = IotProductDeviceTypeEnum.class, message = "设备类型必须是 {value}")
@NotNull(message = "设备类型不能为空")
@@ -44,7 +57,4 @@ public class IotProductSaveReqVO {
@NotNull(message = "数据校验级别不能为空")
private Integer validateType;
- @Schema(description = "产品描述", example = "描述")
- private String description;
-
}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java
new file mode 100644
index 0000000000..95e50a4a27
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotDataBridgeController.java
@@ -0,0 +1,72 @@
+package cn.iocoder.yudao.module.iot.controller.admin.rule;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgePageReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeRespVO;
+import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.IotDataBridgeSaveReqVO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataBridgeDO;
+import cn.iocoder.yudao.module.iot.service.rule.IotDataBridgeService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - IoT 数据桥梁")
+@RestController
+@RequestMapping("/iot/data-bridge")
+@Validated
+public class IotDataBridgeController {
+
+ @Resource
+ private IotDataBridgeService dataBridgeService;
+
+ @PostMapping("/create")
+ @Operation(summary = "创建数据桥梁")
+ @PreAuthorize("@ss.hasPermission('iot:data-bridge:create')")
+ public CommonResult createDataBridge(@Valid @RequestBody IotDataBridgeSaveReqVO createReqVO) {
+ return success(dataBridgeService.createDataBridge(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "更新数据桥梁")
+ @PreAuthorize("@ss.hasPermission('iot:data-bridge:update')")
+ public CommonResult updateDataBridge(@Valid @RequestBody IotDataBridgeSaveReqVO updateReqVO) {
+ dataBridgeService.updateDataBridge(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "删除数据桥梁")
+ @Parameter(name = "id", description = "编号", required = true)
+ @PreAuthorize("@ss.hasPermission('iot:data-bridge:delete')")
+ public CommonResult deleteDataBridge(@RequestParam("id") Long id) {
+ dataBridgeService.deleteDataBridge(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "获得数据桥梁")
+ @Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('iot:data-bridge:query')")
+ public CommonResult getDataBridge(@RequestParam("id") Long id) {
+ IotDataBridgeDO dataBridge = dataBridgeService.getDataBridge(id);
+ return success(BeanUtils.toBean(dataBridge, IotDataBridgeRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "获得数据桥梁分页")
+ @PreAuthorize("@ss.hasPermission('iot:data-bridge:query')")
+ public CommonResult> getDataBridgePage(@Valid IotDataBridgePageReqVO pageReqVO) {
+ PageResult pageResult = dataBridgeService.getDataBridgePage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, IotDataBridgeRespVO.class));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotRuleSceneController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotRuleSceneController.java
new file mode 100644
index 0000000000..04e2f4570a
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/IotRuleSceneController.java
@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.iot.controller.admin.rule;
+
+import cn.iocoder.yudao.module.iot.service.rule.IotRuleSceneService;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.annotation.security.PermitAll;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@Tag(name = "管理后台 - IoT 规则场景")
+@RestController
+@RequestMapping("/iot/rule-scene")
+@Validated
+public class IotRuleSceneController {
+
+ @Resource
+ private IotRuleSceneService ruleSceneService;
+
+ @GetMapping("/test")
+ @PermitAll
+ public void test() {
+ ruleSceneService.test();
+ }
+
+}
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java
new file mode 100644
index 0000000000..e4dc36ef9e
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgePageReqVO.java
@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge;
+
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@Schema(description = "管理后台 - IoT 数据桥梁分页 Request VO")
+@Data
+public class IotDataBridgePageReqVO extends PageParam {
+
+ @Schema(description = "桥梁名称", example = "赵六")
+ private String name;
+
+ @Schema(description = "桥梁状态", example = "1")
+ @InEnum(CommonStatusEnum.class)
+ private Integer status;
+
+ @Schema(description = "创建时间")
+ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+ private LocalDateTime[] createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java
new file mode 100644
index 0000000000..38e04b2ebe
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeRespVO.java
@@ -0,0 +1,37 @@
+package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge;
+
+import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeAbstractConfig;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - IoT 数据桥梁 Response VO")
+@Data
+public class IotDataBridgeRespVO {
+
+ @Schema(description = "桥梁编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18564")
+ private Long id;
+
+ @Schema(description = "桥梁名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
+ private String name;
+
+ @Schema(description = "桥梁描述", example = "随便")
+ private String description;
+
+ @Schema(description = "桥梁状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Integer status;
+
+ @Schema(description = "桥梁方向", requiredMode = Schema.RequiredMode.REQUIRED)
+ private Integer direction;
+
+ @Schema(description = "桥梁类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Integer type;
+
+ @Schema(description = "桥梁配置")
+ private IotDataBridgeAbstractConfig config;
+
+ @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
+ private LocalDateTime createTime;
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java
new file mode 100644
index 0000000000..8441701af8
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/IotDataBridgeSaveReqVO.java
@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge;
+
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config.IotDataBridgeAbstractConfig;
+import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeDirectionEnum;
+import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - IoT 数据桥梁新增/修改 Request VO")
+@Data
+public class IotDataBridgeSaveReqVO {
+
+ @Schema(description = "桥梁编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "18564")
+ private Long id;
+
+ @Schema(description = "桥梁名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
+ @NotEmpty(message = "桥梁名称不能为空")
+ private String name;
+
+ @Schema(description = "桥梁描述", example = "随便")
+ private String description;
+
+ @Schema(description = "桥梁状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+ @NotNull(message = "桥梁状态不能为空")
+ @InEnum(CommonStatusEnum.class)
+ private Integer status;
+
+ @Schema(description = "桥梁方向", requiredMode = Schema.RequiredMode.REQUIRED)
+ @NotNull(message = "桥梁方向不能为空")
+ @InEnum(IotDataBridgeDirectionEnum.class)
+ private Integer direction;
+
+ @Schema(description = "桥梁类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "桥梁类型不能为空")
+ @InEnum(IotDataBridgeTypeEnum.class)
+ private Integer type;
+
+ @Schema(description = "桥梁配置")
+ @NotNull(message = "桥梁配置不能为空")
+ private IotDataBridgeAbstractConfig config;
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java
new file mode 100644
index 0000000000..527e79b351
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeAbstractConfig.java
@@ -0,0 +1,35 @@
+package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config;
+
+import cn.iocoder.yudao.module.iot.enums.rule.IotDataBridgeTypeEnum;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import lombok.Data;
+
+/**
+ * IoT IotDataBridgeConfig 抽象类
+ *
+ * 用于表示数据桥梁配置数据的通用类型,根据具体的 "type" 字段动态映射到对应的子类
+ * 提供多态支持,适用于不同类型的数据结构序列化和反序列化场景。
+ *
+ * @author HUIHUI
+ */
+@Data
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true)
+@JsonSubTypes({
+ @JsonSubTypes.Type(value = IotDataBridgeHttpConfig.class, name = "1"),
+ @JsonSubTypes.Type(value = IotDataBridgeMqttConfig.class, name = "10"),
+ @JsonSubTypes.Type(value = IotDataBridgeRedisStreamMQConfig.class, name = "21"),
+ @JsonSubTypes.Type(value = IotDataBridgeRocketMQConfig.class, name = "30"),
+ @JsonSubTypes.Type(value = IotDataBridgeRabbitMQConfig.class, name = "31"),
+ @JsonSubTypes.Type(value = IotDataBridgeKafkaMQConfig.class, name = "32"),
+})
+public abstract class IotDataBridgeAbstractConfig {
+
+ /**
+ * 配置类型
+ *
+ * 枚举 {@link IotDataBridgeTypeEnum#getType()}
+ */
+ private String type;
+
+}
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java
new file mode 100644
index 0000000000..eca35c76ec
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeHttpConfig.java
@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config;
+
+import lombok.Data;
+
+import java.util.Map;
+
+/**
+ * IoT HTTP 配置 {@link IotDataBridgeAbstractConfig} 实现类
+ *
+ * @author HUIHUI
+ */
+@Data
+public class IotDataBridgeHttpConfig extends IotDataBridgeAbstractConfig {
+
+ /**
+ * 请求 URL
+ */
+ private String url;
+ /**
+ * 请求方法
+ */
+ private String method;
+ /**
+ * 请求头
+ */
+ private Map headers;
+ /**
+ * 请求参数
+ */
+ private Map query;
+ /**
+ * 请求体
+ */
+ private String body;
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java
new file mode 100644
index 0000000000..1201214d12
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeKafkaMQConfig.java
@@ -0,0 +1,35 @@
+package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config;
+
+import lombok.Data;
+
+/**
+ * IoT Kafka 配置 {@link IotDataBridgeAbstractConfig} 实现类
+ *
+ * @author HUIHUI
+ */
+@Data
+public class IotDataBridgeKafkaMQConfig extends IotDataBridgeAbstractConfig {
+
+ /**
+ * Kafka 服务器地址
+ */
+ private String bootstrapServers;
+ /**
+ * 用户名
+ */
+ private String username;
+ /**
+ * 密码
+ */
+ private String password;
+ /**
+ * 是否启用 SSL
+ */
+ private Boolean ssl;
+
+ /**
+ * 主题
+ */
+ private String topic;
+
+}
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java
new file mode 100644
index 0000000000..448b21501d
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeMqttConfig.java
@@ -0,0 +1,34 @@
+package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config;
+
+import lombok.Data;
+
+/**
+ * IoT MQTT 配置 {@link IotDataBridgeAbstractConfig} 实现类
+ *
+ * @author HUIHUI
+ */
+@Data
+public class IotDataBridgeMqttConfig extends IotDataBridgeAbstractConfig {
+
+ /**
+ * MQTT 服务器地址
+ */
+ private String url;
+ /**
+ * 用户名
+ */
+ private String username;
+ /**
+ * 密码
+ */
+ private String password;
+ /**
+ * 客户端编号
+ */
+ private String clientId;
+ /**
+ * 主题
+ */
+ private String topic;
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java
new file mode 100644
index 0000000000..2c247d1d58
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRabbitMQConfig.java
@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config;
+
+import lombok.Data;
+
+/**
+ * IoT RabbitMQ 配置 {@link IotDataBridgeAbstractConfig} 实现类
+ *
+ * @author HUIHUI
+ */
+@Data
+public class IotDataBridgeRabbitMQConfig extends IotDataBridgeAbstractConfig {
+
+ /**
+ * RabbitMQ 服务器地址
+ */
+ private String host;
+ /**
+ * 端口
+ */
+ private Integer port;
+ /**
+ * 虚拟主机
+ */
+ private String virtualHost;
+ /**
+ * 用户名
+ */
+ private String username;
+ /**
+ * 密码
+ */
+ private String password;
+
+ /**
+ * 交换机名称
+ */
+ private String exchange;
+ /**
+ * 路由键
+ */
+ private String routingKey;
+ /**
+ * 队列名称
+ */
+ private String queue;
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamMQConfig.java
new file mode 100644
index 0000000000..3c9bb330fe
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRedisStreamMQConfig.java
@@ -0,0 +1,35 @@
+package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config;
+
+import lombok.Data;
+
+// TODO @puhui999:MQ 可以去掉哈。stream 更精准
+/**
+ * IoT Redis Stream 配置 {@link IotDataBridgeAbstractConfig} 实现类
+ *
+ * @author HUIHUI
+ */
+@Data
+public class IotDataBridgeRedisStreamMQConfig extends IotDataBridgeAbstractConfig {
+
+ /**
+ * Redis 服务器地址
+ */
+ private String host;
+ /**
+ * 端口
+ */
+ private Integer port;
+ /**
+ * 密码
+ */
+ private String password;
+ /**
+ * 数据库索引
+ */
+ private Integer database;
+
+ /**
+ * 主题
+ */
+ private String topic;
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java
new file mode 100644
index 0000000000..e23e3061a1
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/databridge/config/IotDataBridgeRocketMQConfig.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.iot.controller.admin.rule.vo.databridge.config;
+
+import lombok.Data;
+
+/**
+ * IoT RocketMQ 配置 {@link IotDataBridgeAbstractConfig} 实现类
+ *
+ * @author HUIHUI
+ */
+@Data
+public class IotDataBridgeRocketMQConfig extends IotDataBridgeAbstractConfig {
+
+ /**
+ * RocketMQ 名称服务器地址
+ */
+ private String nameServer;
+ /**
+ * 访问密钥
+ */
+ private String accessKey;
+ /**
+ * 秘密钥匙
+ */
+ private String secretKey;
+
+ /**
+ * 生产者组
+ */
+ private String group;
+ /**
+ * 主题
+ */
+ private String topic;
+ /**
+ * 标签
+ */
+ private String tags;
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/package-info.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/package-info.java
new file mode 100644
index 0000000000..f397e0acdb
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/package-info.java
@@ -0,0 +1,2 @@
+// TODO @芋艿:占位
+package cn.iocoder.yudao.module.iot.controller.admin.rule.vo;
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java
new file mode 100644
index 0000000000..a9c195656c
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/IotStatisticsController.java
@@ -0,0 +1,79 @@
+package cn.iocoder.yudao.module.iot.controller.admin.statistics;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsDeviceMessageSummaryRespVO;
+import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.statistics.vo.IotStatisticsSummaryRespVO;
+import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStateEnum;
+import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
+import cn.iocoder.yudao.module.iot.service.device.data.IotDeviceLogService;
+import cn.iocoder.yudao.module.iot.service.product.IotProductCategoryService;
+import cn.iocoder.yudao.module.iot.service.product.IotProductService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.time.LocalDateTime;
+import java.util.Map;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.*;
+
+@Tag(name = "管理后台 - IoT 数据统计")
+@RestController
+@RequestMapping("/iot/statistics")
+@Validated
+public class IotStatisticsController {
+
+ @Resource
+ private IotDeviceService deviceService;
+ @Resource
+ private IotProductCategoryService productCategoryService;
+ @Resource
+ private IotProductService productService;
+ @Resource
+ private IotDeviceLogService deviceLogService;
+
+ @GetMapping("/get-summary")
+ @Operation(summary = "获取 IoT 数据统计")
+ public CommonResult getIotStatisticsSummary(){
+ IotStatisticsSummaryRespVO respVO = new IotStatisticsSummaryRespVO();
+ // 1.1 获取总数
+ respVO.setProductCategoryCount(productCategoryService.getProductCategoryCount(null));
+ respVO.setProductCount(productService.getProductCount(null));
+ respVO.setDeviceCount(deviceService.getDeviceCount(null));
+ respVO.setDeviceMessageCount(deviceLogService.getDeviceLogCount(null));
+ // 1.2 获取今日新增数量
+ // TODO @super:使用 LocalDateTimeUtils.getToday()
+ LocalDateTime todayStart = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0);
+ respVO.setProductCategoryTodayCount(productCategoryService.getProductCategoryCount(todayStart));
+ respVO.setProductTodayCount(productService.getProductCount(todayStart));
+ respVO.setDeviceTodayCount(deviceService.getDeviceCount(todayStart));
+ respVO.setDeviceMessageTodayCount(deviceLogService.getDeviceLogCount(todayStart));
+
+ // 2. 获取各个品类下设备数量统计
+ respVO.setProductCategoryDeviceCounts(productCategoryService.getProductCategoryDeviceCountMap());
+
+ // 3. 获取设备状态数量统计
+ Map deviceCountMap = deviceService.getDeviceCountMapByState();
+ respVO.setDeviceOnlineCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.ONLINE.getState(), 0L));
+ respVO.setDeviceOfflineCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.OFFLINE.getState(), 0L));
+ respVO.setDeviceInactiveCount(deviceCountMap.getOrDefault(IotDeviceStateEnum.INACTIVE.getState(), 0L));
+ return success(respVO);
+ }
+
+ // TODO @super:要不干掉 IotStatisticsReqVO 参数,直接使用 @RequestParam 接收,简单一些。
+ @GetMapping("/get-log-summary")
+ @Operation(summary = "获取 IoT 设备上下行消息数据统计")
+ public CommonResult getIotStatisticsDeviceMessageSummary(
+ @Valid IotStatisticsReqVO reqVO) {
+ return success(new IotStatisticsDeviceMessageSummaryRespVO()
+ .setDownstreamCounts(deviceLogService.getDeviceLogUpCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime()))
+ .setDownstreamCounts((deviceLogService.getDeviceLogDownCountByHour(null, reqVO.getStartTime(), reqVO.getEndTime()))));
+ }
+
+}
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryRespVO.java
new file mode 100644
index 0000000000..15d2abccc6
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/statistics/vo/IotStatisticsDeviceMessageSummaryRespVO.java
@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.iot.controller.admin.statistics.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+import java.util.Map;
+
+@Schema(description = "管理后台 - IoT 设备上下行消息数量统计 Response VO")
+@Data
+public class IotStatisticsDeviceMessageSummaryRespVO {
+
+ @Schema(description = "每小时上行数据数量统计")
+ private List