groupIds;
/**
* 产品编号
@@ -66,13 +80,6 @@ public class IotDeviceDO extends BaseDO {
* 冗余 {@link IotProductDO#getDeviceType()}
*/
private Integer deviceType;
-
- /**
- * 设备状态
- *
- * 枚举 {@link IotDeviceStatusEnum}
- */
- private Integer status;
/**
* 网关设备编号
*
@@ -82,6 +89,13 @@ public class IotDeviceDO extends BaseDO {
*/
private Long gatewayId;
+ /**
+ * 设备状态
+ *
+ * 枚举 {@link IotDeviceStatusEnum}
+ */
+ private Integer status;
+
/**
* 设备状态最后更新时间
*/
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceGroupDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceGroupDO.java
new file mode 100644
index 0000000000..44c471216d
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceGroupDO.java
@@ -0,0 +1,44 @@
+package cn.iocoder.yudao.module.iot.dal.dataobject.device;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+/**
+ * IoT 设备分组 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("iot_device_group")
+@KeySequence("iot_device_group_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class IotDeviceGroupDO extends BaseDO {
+
+ /**
+ * 分组 ID
+ */
+ @TableId
+ private Long id;
+ /**
+ * 分组名字
+ */
+ private String name;
+ /**
+ * 分组状态
+ *
+ * 枚举 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}
+ */
+ private Integer status;
+ /**
+ * 分组描述
+ */
+ 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/dal/dataobject/plugininfo/PluginInfoDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java
new file mode 100644
index 0000000000..043cdb9c81
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java
@@ -0,0 +1,83 @@
+package cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+/**
+ * IoT 插件信息 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("iot_plugin_info")
+@KeySequence("iot_plugin_info_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class PluginInfoDO extends BaseDO {
+
+ /**
+ * 主键 ID
+ */
+ @TableId
+ private Long id;
+ // TODO @haohao:这个是不是改成类似 key 之类的字段哈?
+ /**
+ * 插件包 ID
+ */
+ private String pluginId;
+ /**
+ * 插件名称
+ */
+ private String name;
+ /**
+ * 描述
+ */
+ private String description;
+ /**
+ * 部署方式
+ */
+ // TODO @haohao:枚举
+ private Integer deployType;
+ /**
+ * 插件包文件名
+ */
+ // TODO @haohao:是不是叫 fileName 哈?避免后续有别的字段,类似 fileUrl?
+ private String file;
+ /**
+ * 插件版本
+ */
+ private String version;
+ /**
+ * 插件类型
+ */
+ // TODO @haohao:枚举
+ private Integer type;
+ /**
+ * 设备插件协议类型
+ */
+ private String protocol;
+ /**
+ * 状态
+ */
+ // TODO @haohao:枚举
+ private Integer status;
+ /**
+ * 插件配置项描述信息
+ */
+ private String configSchema;
+ /**
+ * 插件配置信息
+ */
+ private String config;
+ /**
+ * 插件脚本
+ */
+ 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/dal/dataobject/plugininstance/PluginInstanceDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java
new file mode 100644
index 0000000000..17f295a55f
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java
@@ -0,0 +1,51 @@
+package cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+// TODO @haohao:一些必要的关联、枚举
+/**
+ * IoT 插件实例 DO
+ *
+ * @author 芋道源码
+ */
+@TableName("iot_plugin_instance")
+@KeySequence("iot_plugin_instance_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class PluginInstanceDO extends BaseDO {
+
+ /**
+ * 主键ID
+ */
+ @TableId
+ private Long id;
+ /**
+ * 插件主程序 ID
+ */
+ private String mainId;
+ /**
+ * 插件id
+ */
+ private Long pluginId;
+ /**
+ * 插件主程序所在ip
+ */
+ private String ip;
+ /**
+ * 插件主程序端口
+ */
+ private Integer port;
+ /**
+ * 心跳时间,心路时间超过 30 秒需要剔除
+ */
+ private Long heartbeatAt;
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java
new file mode 100644
index 0000000000..1f80ae4558
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceGroupMapper.java
@@ -0,0 +1,35 @@
+package cn.iocoder.yudao.module.iot.dal.mysql.device;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * IoT 设备分组 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface IotDeviceGroupMapper extends BaseMapperX {
+
+ default PageResult selectPage(IotDeviceGroupPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .likeIfPresent(IotDeviceGroupDO::getName, reqVO.getName())
+ .betweenIfPresent(IotDeviceGroupDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(IotDeviceGroupDO::getId));
+ }
+
+ default List selectListByStatus(Integer status) {
+ return selectList(IotDeviceGroupDO::getStatus, status);
+ }
+
+ default IotDeviceGroupDO selectByName(String name) {
+ return selectOne(IotDeviceGroupDO::getName, 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/dal/mysql/device/IotDeviceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java
index 4d8315eb3c..893d7eefda 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.iot.dal.mysql.device;
+import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
@@ -7,6 +8,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePa
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
import org.apache.ibatis.annotations.Mapper;
+import java.util.List;
+
/**
* IoT 设备 Mapper
*
@@ -15,30 +18,21 @@ import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface IotDeviceMapper extends BaseMapperX {
- // TODO @haohao:可能多余的查询条件,要去掉哈
default PageResult selectPage(IotDevicePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX()
- .eqIfPresent(IotDeviceDO::getDeviceKey, reqVO.getDeviceKey())
.likeIfPresent(IotDeviceDO::getDeviceName, reqVO.getDeviceName())
.eqIfPresent(IotDeviceDO::getProductId, reqVO.getProductId())
- .eqIfPresent(IotDeviceDO::getProductKey, reqVO.getProductKey())
.eqIfPresent(IotDeviceDO::getDeviceType, reqVO.getDeviceType())
.likeIfPresent(IotDeviceDO::getNickname, reqVO.getNickname())
- .eqIfPresent(IotDeviceDO::getGatewayId, reqVO.getGatewayId())
.eqIfPresent(IotDeviceDO::getStatus, reqVO.getStatus())
- .betweenIfPresent(IotDeviceDO::getStatusLastUpdateTime, reqVO.getStatusLastUpdateTime())
- .betweenIfPresent(IotDeviceDO::getLastOnlineTime, reqVO.getLastOnlineTime())
- .betweenIfPresent(IotDeviceDO::getLastOfflineTime, reqVO.getLastOfflineTime())
- .betweenIfPresent(IotDeviceDO::getActiveTime, reqVO.getActiveTime())
- .eqIfPresent(IotDeviceDO::getDeviceSecret, reqVO.getDeviceSecret())
- .eqIfPresent(IotDeviceDO::getMqttClientId, reqVO.getMqttClientId())
- .likeIfPresent(IotDeviceDO::getMqttUsername, reqVO.getMqttUsername())
- .eqIfPresent(IotDeviceDO::getMqttPassword, reqVO.getMqttPassword())
- .eqIfPresent(IotDeviceDO::getAuthType, reqVO.getAuthType())
- .betweenIfPresent(IotDeviceDO::getCreateTime, reqVO.getCreateTime())
+ .apply(ObjectUtil.isNotNull(reqVO.getGroupId()), "FIND_IN_SET(" + reqVO.getGroupId() + ",group_ids) > 0")
.orderByDesc(IotDeviceDO::getId));
}
+ default IotDeviceDO selectByDeviceName(String deviceName) {
+ return selectOne(IotDeviceDO::getDeviceName, deviceName);
+ }
+
default IotDeviceDO selectByProductKeyAndDeviceName(String productKey, String deviceName) {
return selectOne(IotDeviceDO::getProductKey, productKey,
IotDeviceDO::getDeviceName, deviceName);
@@ -51,4 +45,19 @@ public interface IotDeviceMapper extends BaseMapperX {
default Long selectCountByProductId(Long productId) {
return selectCount(IotDeviceDO::getProductId, productId);
}
+
+ default IotDeviceDO selectByDeviceKey(String deviceKey) {
+ return selectOne(IotDeviceDO::getDeviceKey, deviceKey);
+ }
+
+ default List selectList(Integer deviceType) {
+ return selectList(IotDeviceDO::getDeviceType, deviceType);
+ }
+
+ default Long selectCountByGroupId(Long groupId) {
+ return selectCount(new LambdaQueryWrapperX()
+ .apply("FIND_IN_SET(" + groupId + ",group_ids) > 0")
+ .orderByDesc(IotDeviceDO::getId));
+ }
+
}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininfo/PluginInfoMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininfo/PluginInfoMapper.java
new file mode 100644
index 0000000000..69c0bd3929
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininfo/PluginInfoMapper.java
@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.module.iot.dal.mysql.plugininfo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.*;
+
+/**
+ * IoT 插件信息 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface PluginInfoMapper extends BaseMapperX {
+
+ default PageResult selectPage(PluginInfoPageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .eqIfPresent(PluginInfoDO::getPluginId, reqVO.getPluginId())
+ .likeIfPresent(PluginInfoDO::getName, reqVO.getName())
+ .eqIfPresent(PluginInfoDO::getDescription, reqVO.getDescription())
+ .eqIfPresent(PluginInfoDO::getDeployType, reqVO.getDeployType())
+ .eqIfPresent(PluginInfoDO::getFile, reqVO.getFile())
+ .eqIfPresent(PluginInfoDO::getVersion, reqVO.getVersion())
+ .eqIfPresent(PluginInfoDO::getType, reqVO.getType())
+ .eqIfPresent(PluginInfoDO::getProtocol, reqVO.getProtocol())
+ .eqIfPresent(PluginInfoDO::getStatus, reqVO.getStatus())
+ .eqIfPresent(PluginInfoDO::getConfigSchema, reqVO.getConfigSchema())
+ .eqIfPresent(PluginInfoDO::getConfig, reqVO.getConfig())
+ .eqIfPresent(PluginInfoDO::getScript, reqVO.getScript())
+ .betweenIfPresent(PluginInfoDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(PluginInfoDO::getId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininstance/PluginInstanceMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininstance/PluginInstanceMapper.java
new file mode 100644
index 0000000000..6c7f1d231c
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininstance/PluginInstanceMapper.java
@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.iot.dal.mysql.plugininstance;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;
+import org.apache.ibatis.annotations.Mapper;
+import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*;
+
+/**
+ * IoT 插件实例 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface PluginInstanceMapper extends BaseMapperX {
+
+ default PageResult selectPage(PluginInstancePageReqVO reqVO) {
+ return selectPage(reqVO, new LambdaQueryWrapperX()
+ .eqIfPresent(PluginInstanceDO::getMainId, reqVO.getMainId())
+ .eqIfPresent(PluginInstanceDO::getPluginId, reqVO.getPluginId())
+ .eqIfPresent(PluginInstanceDO::getIp, reqVO.getIp())
+ .eqIfPresent(PluginInstanceDO::getPort, reqVO.getPort())
+ .eqIfPresent(PluginInstanceDO::getHeartbeatAt, reqVO.getHeartbeatAt())
+ .betweenIfPresent(PluginInstanceDO::getCreateTime, reqVO.getCreateTime())
+ .orderByDesc(PluginInstanceDO::getId));
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java
index 8b1744af07..70ad56da80 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductCategoryMapper.java
@@ -21,7 +21,7 @@ public interface IotProductCategoryMapper extends BaseMapperX()
.likeIfPresent(IotProductCategoryDO::getName, reqVO.getName())
.betweenIfPresent(IotProductCategoryDO::getCreateTime, reqVO.getCreateTime())
- .orderByDesc(IotProductCategoryDO::getId));
+ .orderByAsc(IotProductCategoryDO::getSort));
}
default List selectListByStatus(Integer status) {
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java
index eac1d29e8b..aef149ae72 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java
@@ -23,7 +23,7 @@ public interface IotProductMapper extends BaseMapperX {
}
default IotProductDO selectByProductKey(String productKey) {
- return selectOne(new LambdaQueryWrapperX().eq(IotProductDO::getProductKey, productKey));
+ return selectOne(IotProductDO::getProductKey, productKey);
}
}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java
new file mode 100644
index 0000000000..f43b69c12c
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java
@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.iot.framework.plugin;
+
+import org.pf4j.spring.SpringPluginManager;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class SpringConfiguration {
+
+ @Bean
+ public SpringPluginManager pluginManager() {
+ return new SpringPluginManager();
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java
index 4f390ca3be..70fd23014e 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceDataService.java
@@ -15,7 +15,6 @@ import java.util.Map;
*/
public interface IotDeviceDataService {
-
/**
* 保存设备数据
*
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java
new file mode 100644
index 0000000000..45e6ab25ef
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupService.java
@@ -0,0 +1,96 @@
+package cn.iocoder.yudao.module.iot.service.device;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupPageReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupSaveReqVO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;
+import jakarta.validation.Valid;
+
+import java.util.Collection;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
+
+/**
+ * IoT 设备分组 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface IotDeviceGroupService {
+
+ /**
+ * 创建设备分组
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createDeviceGroup(@Valid IotDeviceGroupSaveReqVO createReqVO);
+
+ /**
+ * 更新设备分组
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateDeviceGroup(@Valid IotDeviceGroupSaveReqVO updateReqVO);
+
+ /**
+ * 删除设备分组
+ *
+ * @param id 编号
+ */
+ void deleteDeviceGroup(Long id);
+
+ /**
+ * 校验设备分组是否存在
+ *
+ * @param ids 设备分组 ID 数组
+ */
+ default List validateDeviceGroupExists(Collection ids) {
+ if (CollUtil.isEmpty(ids)) {
+ return List.of();
+ }
+ return convertList(ids, this::validateDeviceGroupExists);
+ }
+
+ /**
+ * 校验设备分组是否存在
+ *
+ * @param id 设备分组 ID
+ * @return 设备分组
+ */
+ IotDeviceGroupDO validateDeviceGroupExists(Long id);
+
+ /**
+ * 获得设备分组
+ *
+ * @param id 编号
+ * @return 设备分组
+ */
+ IotDeviceGroupDO getDeviceGroup(Long id);
+
+ /**
+ * 获得设备分组
+ *
+ * @param name 名称
+ * @return 设备分组
+ */
+ IotDeviceGroupDO getDeviceGroupByName(String name);
+
+ /**
+ * 获得设备分组分页
+ *
+ * @param pageReqVO 分页查询
+ * @return 设备分组分页
+ */
+ PageResult getDeviceGroupPage(IotDeviceGroupPageReqVO pageReqVO);
+
+ /**
+ * 获得设备分组列表
+ *
+ * @param status 状态
+ * @return 设备分组列表
+ */
+ List getDeviceGroupListByStatus(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/service/device/IotDeviceGroupServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupServiceImpl.java
new file mode 100644
index 0000000000..06e5cb11de
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceGroupServiceImpl.java
@@ -0,0 +1,94 @@
+package cn.iocoder.yudao.module.iot.service.device;
+
+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.device.vo.group.IotDeviceGroupPageReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.group.IotDeviceGroupSaveReqVO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;
+import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceGroupMapper;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS;
+import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.DEVICE_GROUP_NOT_EXISTS;
+
+/**
+ * IoT 设备分组 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+public class IotDeviceGroupServiceImpl implements IotDeviceGroupService {
+
+ @Resource
+ private IotDeviceGroupMapper deviceGroupMapper;
+
+ @Resource
+ private IotDeviceService deviceService;
+
+ @Override
+ public Long createDeviceGroup(IotDeviceGroupSaveReqVO createReqVO) {
+ // 插入
+ IotDeviceGroupDO deviceGroup = BeanUtils.toBean(createReqVO, IotDeviceGroupDO.class);
+ deviceGroupMapper.insert(deviceGroup);
+ // 返回
+ return deviceGroup.getId();
+ }
+
+ @Override
+ public void updateDeviceGroup(IotDeviceGroupSaveReqVO updateReqVO) {
+ // 校验存在
+ validateDeviceGroupExists(updateReqVO.getId());
+ // 更新
+ IotDeviceGroupDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceGroupDO.class);
+ deviceGroupMapper.updateById(updateObj);
+ }
+
+ @Override
+ public void deleteDeviceGroup(Long id) {
+ // 1.1 校验存在
+ validateDeviceGroupExists(id);
+ // 1.2 校验是否存在设备
+ if (deviceService.getDeviceCountByGroupId(id) > 0) {
+ throw exception(DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS);
+ }
+
+ // 删除
+ deviceGroupMapper.deleteById(id);
+ }
+
+ @Override
+ public IotDeviceGroupDO validateDeviceGroupExists(Long id) {
+ IotDeviceGroupDO group = deviceGroupMapper.selectById(id);
+ if (group == null) {
+ throw exception(DEVICE_GROUP_NOT_EXISTS);
+ }
+ return group;
+ }
+
+ @Override
+ public IotDeviceGroupDO getDeviceGroup(Long id) {
+ return deviceGroupMapper.selectById(id);
+ }
+
+ @Override
+ public IotDeviceGroupDO getDeviceGroupByName(String name) {
+ return deviceGroupMapper.selectByName(name);
+ }
+
+ @Override
+ public PageResult getDeviceGroupPage(IotDeviceGroupPageReqVO pageReqVO) {
+ return deviceGroupMapper.selectPage(pageReqVO);
+ }
+
+ @Override
+ public List getDeviceGroupListByStatus(Integer status) {
+ return deviceGroupMapper.selectListByStatus(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/service/device/IotDeviceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
index ed4c6a60ce..b8a3511cdd 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
@@ -1,11 +1,18 @@
package cn.iocoder.yudao.module.iot.service.device;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO;
-import jakarta.validation.*;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceUpdateGroupReqVO;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceImportRespVO;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceImportExcelVO;
+import jakarta.validation.Valid;
+
+import javax.annotation.Nullable;
+import java.util.Collection;
+import java.util.List;
/**
* IoT 设备 Service 接口
@@ -30,12 +37,33 @@ public interface IotDeviceService {
void updateDevice(@Valid IotDeviceSaveReqVO updateReqVO);
/**
- * 删除设备
+ * 更新设备状态
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO);
+
+ /**
+ * 更新设备分组
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updateDeviceGroup(@Valid IotDeviceUpdateGroupReqVO updateReqVO);
+
+ /**
+ * 删除单个设备
*
* @param id 编号
*/
void deleteDevice(Long id);
+ /**
+ * 删除多个设备
+ *
+ * @param ids 编号数组
+ */
+ void deleteDeviceList(Collection ids);
+
/**
* 获得设备
*
@@ -45,7 +73,7 @@ public interface IotDeviceService {
IotDeviceDO getDevice(Long id);
/**
- * 获得设备分页
+ * ��得设备分页
*
* @param pageReqVO 分页查询
* @return IoT 设备分页
@@ -53,11 +81,12 @@ public interface IotDeviceService {
PageResult getDevicePage(IotDevicePageReqVO pageReqVO);
/**
- * 更新设备状态
+ * 获得设备列表
*
- * @param updateReqVO 更新信息
+ * @param deviceType 设备类型
+ * @return 设备列表
*/
- void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO);
+ List getDeviceList(@Nullable Integer deviceType);
/**
* 获得设备数量
@@ -67,6 +96,14 @@ public interface IotDeviceService {
*/
Long getDeviceCountByProductId(Long productId);
+ /**
+ * 获得设备数量
+ *
+ * @param groupId 分组编号
+ * @return 设备数量
+ */
+ Long getDeviceCountByGroupId(Long groupId);
+
/**
* 根据产品 key 和设备名称,获得设备信息
*
@@ -75,4 +112,14 @@ public interface IotDeviceService {
* @return 设备信息
*/
IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName);
+
+ /**
+ * 导入设备
+ *
+ * @param importDevices 导入设备列表
+ * @param updateSupport 是否支持更新
+ * @return 导入结果
+ */
+ IotDeviceImportRespVO importDevice(List importDevices, boolean updateSupport);
+
}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
index bb302a22f7..a43521ff92 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
@@ -1,31 +1,36 @@
package cn.iocoder.yudao.module.iot.service.device;
+import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
-import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO;
-import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO;
-import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.*;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceGroupDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper;
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum;
+import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum;
import cn.iocoder.yudao.module.iot.service.product.IotProductService;
import jakarta.annotation.Resource;
+import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
-import java.security.SecureRandom;
+import javax.annotation.Nullable;
import java.time.LocalDateTime;
-import java.util.Base64;
-import java.util.Objects;
-import java.util.UUID;
+import java.util.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
/**
@@ -40,68 +45,212 @@ public class IotDeviceServiceImpl implements IotDeviceService {
@Resource
private IotDeviceMapper deviceMapper;
+
@Resource
private IotProductService productService;
+ @Resource
+ @Lazy // 延迟加载,解决循环依赖
+ private IotDeviceGroupService deviceGroupService;
- /**
- * 创建 IoT 设备
- *
- * @param createReqVO 创建请求 VO
- * @return 设备 ID
- */
@Override
- @Transactional(rollbackFor = Exception.class)
public Long createDevice(IotDeviceSaveReqVO createReqVO) {
// 1.1 校验产品是否存在
IotProductDO product = productService.getProduct(createReqVO.getProductId());
if (product == null) {
throw exception(PRODUCT_NOT_EXISTS);
}
- // 1.2 校验设备名称在同一产品下是否唯一
- if (StrUtil.isBlank(createReqVO.getDeviceName())) {
- createReqVO.setDeviceName(generateUniqueDeviceName(product.getProductKey()));
- } else {
- validateDeviceNameUnique(product.getProductKey(), createReqVO.getDeviceName());
+ // 1.2 校验设备标识是否唯一
+ if (deviceMapper.selectByDeviceKey(createReqVO.getDeviceKey()) != null) {
+ throw exception(DEVICE_KEY_EXISTS);
}
+ // 1.3 校验设备名称在同一产品下是否唯一
+ if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceKey()) != null) {
+ throw exception(DEVICE_NAME_EXISTS);
+ }
+ // 1.4 校验父设备是否为合法网关
+ if (IotProductDeviceTypeEnum.isGateway(product.getDeviceType())
+ && createReqVO.getGatewayId() != null) {
+ validateGatewayDeviceExists(createReqVO.getGatewayId());
+ }
+ // 1.5 校验分组存在
+ deviceGroupService.validateDeviceGroupExists(createReqVO.getGroupIds());
// 2.1 转换 VO 为 DO
- IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class)
- .setProductKey(product.getProductKey())
- .setDeviceType(product.getDeviceType());
- // 2.2 生成并设置必要的字段
- device.setDeviceKey(generateUniqueDeviceKey());
- device.setDeviceSecret(generateDeviceSecret());
- device.setMqttClientId(generateMqttClientId());
- device.setMqttUsername(generateMqttUsername(device.getDeviceName(), device.getProductKey()));
- device.setMqttPassword(generateMqttPassword());
- // 2.3 设置设备状态为未激活
- device.setStatus(IotDeviceStatusEnum.INACTIVE.getStatus());
- device.setStatusLastUpdateTime(LocalDateTime.now());
- // 2.4 插入到数据库
+ IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class, o -> {
+ o.setProductKey(product.getProductKey()).setDeviceType(product.getDeviceType());
+ // 生成并设置必要的字段
+ o.setDeviceSecret(generateDeviceSecret())
+ .setMqttClientId(generateMqttClientId())
+ .setMqttUsername(generateMqttUsername(o.getDeviceName(), o.getProductKey()))
+ .setMqttPassword(generateMqttPassword());
+ // 设置设备状态为未激活
+ o.setStatus(IotDeviceStatusEnum.INACTIVE.getStatus()).setStatusLastUpdateTime(LocalDateTime.now());
+ });
+ // 2.2 插入到数据库
deviceMapper.insert(device);
return device.getId();
}
- /**
- * 校验设备名称在同一产品下是否唯一
- *
- * @param productKey 产品 Key
- * @param deviceName 设备名称
- */
- private void validateDeviceNameUnique(String productKey, String deviceName) {
- IotDeviceDO existingDevice = deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName);
- if (existingDevice != null) {
- throw exception(DEVICE_NAME_EXISTS);
+ @Override
+ public void updateDevice(IotDeviceSaveReqVO updateReqVO) {
+ updateReqVO.setDeviceKey(null).setDeviceName(null).setProductId(null); // 不允许更新
+ // 1.1 校验存在
+ IotDeviceDO device = validateDeviceExists(updateReqVO.getId());
+ // 1.2 校验父设备是否为合法网关
+ if (IotProductDeviceTypeEnum.isGateway(device.getDeviceType())
+ && updateReqVO.getGatewayId() != null) {
+ validateGatewayDeviceExists(updateReqVO.getGatewayId());
}
+ // 1.3 校验分组存在
+ deviceGroupService.validateDeviceGroupExists(updateReqVO.getGroupIds());
+
+ // 2. 更新到数据库
+ IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class);
+ deviceMapper.updateById(updateObj);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateDeviceGroup(IotDeviceUpdateGroupReqVO updateReqVO) {
+ // 1.1 校验设备存在
+ List devices = deviceMapper.selectBatchIds(updateReqVO.getIds());
+ if (CollUtil.isEmpty(devices)) {
+ return;
+ }
+ // 1.2 校验分组存在
+ deviceGroupService.validateDeviceGroupExists(updateReqVO.getGroupIds());
+
+ // 3. 更新设备分组
+ deviceMapper.updateBatch(convertList(devices, device -> new IotDeviceDO()
+ .setId(device.getId()).setGroupIds(updateReqVO.getGroupIds())));
+ }
+
+ @Override
+ public void deleteDevice(Long id) {
+ // 1.1 校验存在
+ IotDeviceDO device = validateDeviceExists(id);
+ // 1.2 如果是网关设备,检查是否有子设备
+ if (device.getGatewayId() != null && deviceMapper.selectCountByGatewayId(id) > 0) {
+ throw exception(DEVICE_HAS_CHILDREN);
+ }
+
+ // 2. 删除设备
+ deviceMapper.deleteById(id);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteDeviceList(Collection ids) {
+ // 1.1 校验存在
+ if (CollUtil.isEmpty(ids)) {
+ return;
+ }
+ List devices = deviceMapper.selectBatchIds(ids);
+ if (CollUtil.isEmpty(devices)) {
+ return;
+ }
+ // 1.2 校验网关设备是否存在
+ for (IotDeviceDO device : devices) {
+ if (device.getGatewayId() != null && deviceMapper.selectCountByGatewayId(device.getId()) > 0) {
+ throw exception(DEVICE_HAS_CHILDREN);
+ }
+ }
+
+ // 2. 删除设备
+ deviceMapper.deleteByIds(ids);
}
/**
- * 生成唯一的 deviceKey
+ * 校验设备是否存在
+ *
+ * @param id 设备 ID
+ * @return 设备对象
+ */
+ private IotDeviceDO validateDeviceExists(Long id) {
+ IotDeviceDO device = deviceMapper.selectById(id);
+ if (device == null) {
+ throw exception(DEVICE_NOT_EXISTS);
+ }
+ return device;
+ }
+
+ /**
+ * 校验网关设备是否存在
+ *
+ * @param id 设备 ID
+ */
+ private void validateGatewayDeviceExists(Long id) {
+ IotDeviceDO device = deviceMapper.selectById(id);
+ if (device == null) {
+ throw exception(DEVICE_GATEWAY_NOT_EXISTS);
+ }
+ if (!IotProductDeviceTypeEnum.isGateway(device.getDeviceType())) {
+ throw exception(DEVICE_NOT_GATEWAY);
+ }
+ }
+
+ @Override
+ public IotDeviceDO getDevice(Long id) {
+ return deviceMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getDevicePage(IotDevicePageReqVO pageReqVO) {
+ return deviceMapper.selectPage(pageReqVO);
+ }
+
+ @Override
+ public List getDeviceList(@Nullable Integer deviceType) {
+ return deviceMapper.selectList(deviceType);
+ }
+
+ @Override
+ public void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO) {
+ // 1. 校验存在
+ IotDeviceDO device = validateDeviceExists(updateReqVO.getId());
+
+ // 2.1 更新状态和更新时间
+ IotDeviceDO updateDevice = BeanUtils.toBean(updateReqVO, IotDeviceDO.class)
+ .setStatusLastUpdateTime(LocalDateTime.now());
+ // 2.2 更新状态相关时间
+ if (Objects.equals(device.getStatus(), IotDeviceStatusEnum.INACTIVE.getStatus())
+ && Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) {
+ // 从未激活到在线,设置激活时间和最后上线时间
+ updateDevice.setActiveTime(LocalDateTime.now()).setLastOnlineTime(LocalDateTime.now());
+ } else if (Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) {
+ // 如果是上线,设置最后上线时间
+ updateDevice.setLastOnlineTime(LocalDateTime.now());
+ } else if (Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.OFFLINE.getStatus())) {
+ // 如果是离线,设置最后离线时间
+ updateDevice.setLastOfflineTime(LocalDateTime.now());
+ }
+ // 2.3 更新到数据库
+ deviceMapper.updateById(updateDevice);
+ }
+
+ @Override
+ public Long getDeviceCountByProductId(Long productId) {
+ return deviceMapper.selectCountByProductId(productId);
+ }
+
+ @Override
+ public Long getDeviceCountByGroupId(Long groupId) {
+ return deviceMapper.selectCountByGroupId(groupId);
+ }
+
+ @Override
+ @TenantIgnore
+ public IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName) {
+ return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName);
+ }
+
+ /**
+ * 生成 deviceKey
*
* @return 生成的 deviceKey
*/
- private String generateUniqueDeviceKey() {
- return UUID.randomUUID().toString();
+ private String generateDeviceKey() {
+ return RandomUtil.randomString(16);
}
/**
@@ -119,7 +268,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
* @return 生成的 MQTT Client ID
*/
private String generateMqttClientId() {
- return UUID.randomUUID().toString();
+ return IdUtil.fastSimpleUUID();
}
/**
@@ -139,119 +288,77 @@ public class IotDeviceServiceImpl implements IotDeviceService {
* @return 生成的 MQTT Password
*/
private String generateMqttPassword() {
- // TODO @浩浩:这里的 StrUtil 随机字符串?
- SecureRandom secureRandom = new SecureRandom();
- byte[] passwordBytes = new byte[32]; // 256 位的随机数
- secureRandom.nextBytes(passwordBytes);
- return Base64.getUrlEncoder().withoutPadding().encodeToString(passwordBytes);
+ return RandomUtil.randomString(32);
}
- /**
- * 生成唯一的 DeviceName
- *
- * @param productKey 产品标识
- * @return 生成的唯一 DeviceName
- */
- private String generateUniqueDeviceName(String productKey) {
- for (int i = 0; i < Short.MAX_VALUE; i++) {
- String deviceName = IdUtil.fastSimpleUUID().substring(0, 20);
- if (deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName) != null) {
- return deviceName;
+ @Override
+ @Transactional(rollbackFor = Exception.class) // 添加事务,异常则回滚所有导入
+ public IotDeviceImportRespVO importDevice(List importDevices, boolean updateSupport) {
+ // 1. 参数校验
+ if (CollUtil.isEmpty(importDevices)) {
+ throw exception(DEVICE_IMPORT_LIST_IS_EMPTY);
+ }
+
+ // 2. 遍历,逐个创建 or 更新
+ IotDeviceImportRespVO respVO = IotDeviceImportRespVO.builder().createDeviceNames(new ArrayList<>())
+ .updateDeviceNames(new ArrayList<>()).failureDeviceNames(new LinkedHashMap<>()).build();
+ importDevices.forEach(importDevice -> {
+ try {
+ // 2.1.1 校验字段是否符合要求
+ try {
+ ValidationUtils.validate(importDevice);
+ } catch (ConstraintViolationException ex){
+ respVO.getFailureDeviceNames().put(importDevice.getDeviceName(), ex.getMessage());
+ return;
+ }
+ // 2.1.2 校验产品是否存在
+ IotProductDO product = productService.validateProductExists(importDevice.getProductKey());
+ // 2.1.3 校验父设备是否存在
+ Long gatewayId = null;
+ if (StrUtil.isNotEmpty(importDevice.getParentDeviceName())) {
+ IotDeviceDO gatewayDevice = deviceMapper.selectByDeviceName(importDevice.getParentDeviceName());
+ if (gatewayDevice == null) {
+ throw exception(DEVICE_GATEWAY_NOT_EXISTS);
+ }
+ if (!IotProductDeviceTypeEnum.isGateway(gatewayDevice.getDeviceType())) {
+ throw exception(DEVICE_NOT_GATEWAY);
+ }
+ gatewayId = gatewayDevice.getId();
+ }
+ // 2.1.4 校验设备分组是否存在
+ Set groupIds = new HashSet<>();
+ if (StrUtil.isNotEmpty(importDevice.getGroupNames())) {
+ String[] groupNames = importDevice.getGroupNames().split(",");
+ for (String groupName : groupNames) {
+ IotDeviceGroupDO group = deviceGroupService.getDeviceGroupByName(groupName);
+ if (group == null) {
+ throw exception(DEVICE_GROUP_NOT_EXISTS);
+ }
+ groupIds.add(group.getId());
+ }
+ }
+
+ // 2.2.1 判断如果不存在,在进行插入
+ IotDeviceDO existDevice = deviceMapper.selectByDeviceName(importDevice.getDeviceName());
+ if (existDevice == null) {
+ createDevice(new IotDeviceSaveReqVO()
+ .setDeviceName(importDevice.getDeviceName()).setDeviceKey(generateDeviceKey())
+ .setProductId(product.getId()).setGatewayId(gatewayId).setGroupIds(groupIds));
+ respVO.getCreateDeviceNames().add(importDevice.getDeviceName());
+ return;
+ }
+ // 2.2.2 如果存在,判断是否允许更新
+ if (updateSupport) {
+ throw exception(DEVICE_KEY_EXISTS);
+ }
+ updateDevice(new IotDeviceSaveReqVO().setId(existDevice.getId())
+ .setGatewayId(gatewayId).setGroupIds(groupIds));
+ respVO.getUpdateDeviceNames().add(importDevice.getDeviceName());
+ } catch (ServiceException ex) {
+ respVO.getFailureDeviceNames().put(importDevice.getDeviceName(), ex.getMessage());
}
- }
- throw new IllegalArgumentException("生成 DeviceName 失败");
- }
-
- @Override
- @Transactional(rollbackFor = Exception.class)
- public void updateDevice(IotDeviceSaveReqVO updateReqVO) {
- // 1. 校验存在
- validateDeviceExists(updateReqVO.getId());
-
- // 2. 更新到数据库
- IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class)
- .setDeviceName(null).setProductId(null); // 设备名称 和 产品 ID 不能修改
- deviceMapper.updateById(updateObj);
- }
-
- @Override
- @Transactional(rollbackFor = Exception.class)
- public void deleteDevice(Long id) {
- // 1.1 校验存在
- IotDeviceDO device = validateDeviceExists(id);
- // 1.2 如果是网关设备,检查是否有子设备
- if (device.getGatewayId() != null && deviceMapper.selectCountByGatewayId(id) > 0) {
- throw exception(DEVICE_HAS_CHILDREN);
- }
-
- // 2. 删除设备
- deviceMapper.deleteById(id);
- }
-
- /**
- * 校验设备是否存在
- *
- * @param id 设备 ID
- * @return 设备对象
- */
- private IotDeviceDO validateDeviceExists(Long id) {
- IotDeviceDO device = deviceMapper.selectById(id);
- if (device == null) {
- throw exception(DEVICE_NOT_EXISTS);
- }
- return device;
- }
-
- @Override
- public IotDeviceDO getDevice(Long id) {
- IotDeviceDO device = deviceMapper.selectById(id);
- if (device == null) {
- throw exception(DEVICE_NOT_EXISTS);
- }
- return device;
- }
-
- @Override
- public PageResult getDevicePage(IotDevicePageReqVO pageReqVO) {
- return deviceMapper.selectPage(pageReqVO);
- }
-
- @Override
- public void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO) {
- // 1. 校验存在
- IotDeviceDO device = validateDeviceExists(updateReqVO.getId());
-
- // 2.1 更新状态和更新时间
- IotDeviceDO updateDevice = BeanUtils.toBean(updateReqVO, IotDeviceDO.class);
- // 2.2 更新状态相关时间
- if (Objects.equals(device.getStatus(), IotDeviceStatusEnum.INACTIVE.getStatus())
- && Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) {
- // 从未激活到在线,设置激活时间和最后上线时间
- updateDevice.setActiveTime(LocalDateTime.now());
- updateDevice.setLastOnlineTime(LocalDateTime.now());
- } else if (Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) {
- // 如果是上线,设置最后上线时间
- updateDevice.setLastOnlineTime(LocalDateTime.now());
- } else if (Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.OFFLINE.getStatus())) {
- // 如果是离线,设置最后离线时间
- updateDevice.setLastOfflineTime(LocalDateTime.now());
- }
-
- // 2.3 设置状态更新时间
- updateDevice.setStatusLastUpdateTime(LocalDateTime.now());
- // 2.4 更新到数据库
- deviceMapper.updateById(updateDevice);
- }
-
- @Override
- public Long getDeviceCountByProductId(Long productId) {
- return deviceMapper.selectCountByProductId(productId);
- }
-
- @Override
- @TenantIgnore
- public IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName) {
- return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName);
+ });
+ return respVO;
}
}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java
new file mode 100644
index 0000000000..f9ae486772
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java
@@ -0,0 +1,72 @@
+package cn.iocoder.yudao.module.iot.service.plugininfo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
+import jakarta.validation.Valid;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.InputStream;
+
+/**
+ * IoT 插件信息 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface PluginInfoService {
+
+ /**
+ * 创建IoT 插件信息
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createPluginInfo(@Valid PluginInfoSaveReqVO createReqVO);
+
+ /**
+ * 更新IoT 插件信息
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updatePluginInfo(@Valid PluginInfoSaveReqVO updateReqVO);
+
+ /**
+ * 删除IoT 插件信息
+ *
+ * @param id 编号
+ */
+ void deletePluginInfo(Long id);
+
+ /**
+ * 获得IoT 插件信息
+ *
+ * @param id 编号
+ * @return IoT 插件信息
+ */
+ PluginInfoDO getPluginInfo(Long id);
+
+ /**
+ * 获得IoT 插件信息分页
+ *
+ * @param pageReqVO 分页查询
+ * @return IoT 插件信息分页
+ */
+ PageResult getPluginInfoPage(PluginInfoPageReqVO pageReqVO);
+
+ /**
+ * 上传插件的 JAR 包
+ *
+ * @param id 插件id
+ * @param file 文件
+ */
+ void uploadJar(Long id, MultipartFile file);
+
+ /**
+ * 更新插件的状态
+ *
+ * @param id 插件id
+ * @param status 状态
+ */
+ void updatePluginStatus(Long id, 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/service/plugininfo/PluginInfoServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java
new file mode 100644
index 0000000000..65ad6ad1db
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java
@@ -0,0 +1,267 @@
+package cn.iocoder.yudao.module.iot.service.plugininfo;
+
+import cn.hutool.core.io.IoUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.infra.api.file.FileApi;
+import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
+import cn.iocoder.yudao.module.iot.dal.mysql.plugininfo.PluginInfoMapper;
+import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.Resource;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.pf4j.PluginDescriptor;
+import org.pf4j.PluginState;
+import org.pf4j.PluginWrapper;
+import org.pf4j.spring.SpringPluginManager;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.nio.file.Path;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
+
+/**
+ * IoT 插件信息 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+@Slf4j
+public class PluginInfoServiceImpl implements PluginInfoService {
+
+ @Resource
+ private PluginInfoMapper pluginInfoMapper;
+
+ @Resource
+ private SpringPluginManager pluginManager;
+
+ @Resource
+ private FileApi fileApi;
+
+ @Override
+ public Long createPluginInfo(PluginInfoSaveReqVO createReqVO) {
+ // 插入
+ PluginInfoDO pluginInfo = BeanUtils.toBean(createReqVO, PluginInfoDO.class);
+ pluginInfoMapper.insert(pluginInfo);
+ // 返回
+ return pluginInfo.getId();
+ }
+
+ @Override
+ public void updatePluginInfo(PluginInfoSaveReqVO updateReqVO) {
+ // 校验存在
+ validatePluginInfoExists(updateReqVO.getId());
+ // 更新
+ PluginInfoDO updateObj = BeanUtils.toBean(updateReqVO, PluginInfoDO.class);
+ pluginInfoMapper.updateById(updateObj);
+ }
+
+ @Override
+ public void deletePluginInfo(Long id) {
+ // 校验存在
+ PluginInfoDO pluginInfoDO = validatePluginInfoExists(id);
+
+ // 停止插件
+ if (IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) {
+ throw exception(PLUGIN_INFO_DELETE_FAILED_RUNNING);
+ }
+
+ // 卸载插件
+ PluginWrapper plugin = pluginManager.getPlugin(pluginInfoDO.getPluginId());
+ if (plugin != null) {
+ // 查询插件是否是启动状态
+ if (plugin.getPluginState().equals(PluginState.STARTED)) {
+ // 停止插件
+ pluginManager.stopPlugin(plugin.getPluginId());
+ }
+ // 卸载插件
+ pluginManager.unloadPlugin(plugin.getPluginId());
+ }
+
+ // 删除
+ pluginInfoMapper.deleteById(id);
+ }
+
+ private PluginInfoDO validatePluginInfoExists(Long id) {
+ PluginInfoDO pluginInfo = pluginInfoMapper.selectById(id);
+ if (pluginInfo == null) {
+ throw exception(PLUGIN_INFO_NOT_EXISTS);
+ }
+ return pluginInfo;
+ }
+
+ @Override
+ public PluginInfoDO getPluginInfo(Long id) {
+ return pluginInfoMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getPluginInfoPage(PluginInfoPageReqVO pageReqVO) {
+ return pluginInfoMapper.selectPage(pageReqVO);
+ }
+
+ @Override
+ public void uploadJar(Long id, MultipartFile file) {
+ // 1. 校验存在
+ PluginInfoDO pluginInfoDo = validatePluginInfoExists(id);
+
+ // 2. 判断文件名称与插件 ID 是否匹配
+ String pluginId = pluginInfoDo.getPluginId();
+
+ // 3. 停止卸载旧的插件
+ // 3.1. 获取插件信息
+ PluginWrapper plugin = pluginManager.getPlugin(pluginId);
+ if (plugin != null) {
+ // 3.2. 如果插件状态是启动的,停止插件
+ if (plugin.getPluginState().equals(PluginState.STARTED)) {
+ pluginManager.stopPlugin(pluginId);
+ }
+ // 3.3. 卸载插件
+ pluginManager.unloadPlugin(pluginId);
+ }
+
+ // 4. 上传插件
+ String pluginIdNew;
+ try {
+ String path = fileApi.createFile(IoUtil.readBytes(file.getInputStream()));
+ Path pluginPath = Path.of(path);
+ pluginIdNew = pluginManager.loadPlugin(pluginPath);
+ } catch (Exception e) {
+ throw exception(PLUGIN_INSTALL_FAILED);
+ }
+
+ PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginIdNew);
+ if (pluginWrapper == null) {
+ throw exception(PLUGIN_INSTALL_FAILED);
+ }
+
+
+ // 5. 读取配置文件和脚本
+ String configJson = "";
+ String script = "";
+
+ pluginInfoDo.setPluginId(pluginIdNew);
+ pluginInfoDo.setStatus(IotPluginStatusEnum.STOPPED.getStatus());
+ pluginInfoDo.setFile(file.getOriginalFilename());
+ pluginInfoDo.setConfigSchema(configJson);
+ pluginInfoDo.setScript(script);
+
+ PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
+ pluginInfoDo.setVersion(pluginDescriptor.getVersion());
+ pluginInfoDo.setDescription(pluginDescriptor.getPluginDescription());
+ pluginInfoMapper.updateById(pluginInfoDo);
+
+
+ // 5. 读取配置文件和脚本
+// String configJson = "";
+ // String script = "";
+// try (JarFile jarFile = new JarFile(pluginInfoUpdate.getPluginPath())) {
+// // 5.1 获取config文件在jar包中的路径
+// String configFile = "classes/config.json";
+// JarEntry configEntry = jarFile.getJarEntry(configFile);
+//
+// if (configEntry != null) {
+// // 5.2 读取配置文件
+// configJson = IoUtil.read(jarFile.getInputStream(configEntry), Charset.defaultCharset());
+// log.info("configJson:{}", configJson);
+// }
+//
+// // 5.3 读取script.js脚本
+// String scriptFile = "classes/script.js";
+// JarEntry scriptEntity = jarFile.getJarEntry(scriptFile);
+// if (scriptEntity != null) {
+// // 5.4 读取脚本文件
+// script = IoUtil.read(jarFile.getInputStream(scriptEntity), Charset.defaultCharset());
+// log.info("script:{}", script);
+// }
+// } catch (Exception e) {
+// throw exception(PLUGIN_INSTALL_FAILED);
+// }
+
+
+// PluginState pluginState = pluginInfoUpdate.getPluginState();
+// if (pluginState == PluginState.STARTED) {
+// pluginInfoDo.setStatus(IotPluginStatusEnum.RUNNING.getStatus());
+// }
+// pluginInfoDo.setPluginId(pluginInfoUpdate.getPluginId());
+// pluginInfoDo.setFile(file.getOriginalFilename());
+// pluginInfoDo.setConfigSchema(configJson);
+// pluginInfoDo.setScript(script);
+//
+// PluginDescriptor pluginDescriptor = pluginInfoUpdate.getPluginDescriptor();
+// pluginInfoDo.setVersion(pluginDescriptor.getPluginVersion());
+// pluginInfoDo.setDescription(pluginDescriptor.getDescription());
+// pluginInfoMapper.updateById(pluginInfoDo);
+ }
+
+ @Override
+ public void updatePluginStatus(Long id, Integer status) {
+ // 1. 校验存在
+ PluginInfoDO pluginInfoDo = validatePluginInfoExists(id);
+
+ // 插件状态无效
+ if (!IotPluginStatusEnum.contains(status)) {
+ throw exception(PLUGIN_STATUS_INVALID);
+ }
+
+ // 插件包为空
+// String pluginId = pluginInfoDo.getPluginId();
+// if (StrUtil.isBlank(pluginId)) {
+// throw exception(PLUGIN_INFO_NOT_EXISTS);
+// }
+// com.gitee.starblues.core.PluginInfo pluginInfo = pluginOperator.getPluginInfo(pluginId);
+// if (pluginInfo != null) {
+// if (pluginInfoDo.getStatus().equals(IotPluginStatusEnum.RUNNING.getStatus()) && pluginInfo.getPluginState() != PluginState.STARTED) {
+// // 启动插件
+// pluginOperator.start(pluginId);
+// } else if (pluginInfoDo.getStatus().equals(IotPluginStatusEnum.STOPPED.getStatus()) && pluginInfo.getPluginState() == PluginState.STARTED) {
+// // 停止插件
+// pluginOperator.stop(pluginId);
+// }
+// } else {
+// // 已经停止,未获取到插件
+// if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) {
+// throw exception(PLUGIN_STATUS_INVALID);
+// }
+// }
+ pluginInfoDo.setStatus(status);
+ pluginInfoMapper.updateById(pluginInfoDo);
+ }
+
+ @PostConstruct
+ public void init() {
+ Executors.newSingleThreadScheduledExecutor().schedule(this::startPlugins, 3, TimeUnit.SECONDS);
+ }
+
+ @SneakyThrows
+ private void startPlugins() {
+// while (!pluginOperator.inited()) {
+// Thread.sleep(1000L);
+// }
+
+ for (PluginInfoDO pluginInfoDO : pluginInfoMapper.selectList()) {
+ if (!IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) {
+ continue;
+ }
+ log.info("start plugin:{}", pluginInfoDO.getPluginId());
+ try {
+// com.gitee.starblues.core.PluginInfo plugin = pluginOperator.getPluginInfo(pluginInfoDO.getPluginId());
+// if (plugin != null) {
+// pluginOperator.start(plugin.getPluginId());
+// }
+ } catch (Exception e) {
+ log.error("start plugin error", e);
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceService.java
new file mode 100644
index 0000000000..0789c46381
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceService.java
@@ -0,0 +1,54 @@
+package cn.iocoder.yudao.module.iot.service.plugininstance;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstancePageReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstanceSaveReqVO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;
+import jakarta.validation.Valid;
+
+/**
+ * IoT 插件实例 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface PluginInstanceService {
+
+ /**
+ * 创建IoT 插件实例
+ *
+ * @param createReqVO 创建信息
+ * @return 编号
+ */
+ Long createPluginInstance(@Valid PluginInstanceSaveReqVO createReqVO);
+
+ /**
+ * 更新IoT 插件实例
+ *
+ * @param updateReqVO 更新信息
+ */
+ void updatePluginInstance(@Valid PluginInstanceSaveReqVO updateReqVO);
+
+ /**
+ * 删除IoT 插件实例
+ *
+ * @param id 编号
+ */
+ void deletePluginInstance(Long id);
+
+ /**
+ * 获得IoT 插件实例
+ *
+ * @param id 编号
+ * @return IoT 插件实例
+ */
+ PluginInstanceDO getPluginInstance(Long id);
+
+ /**
+ * 获得IoT 插件实例分页
+ *
+ * @param pageReqVO 分页查询
+ * @return IoT 插件实例分页
+ */
+ PageResult getPluginInstancePage(PluginInstancePageReqVO pageReqVO);
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java
new file mode 100644
index 0000000000..405efe1636
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java
@@ -0,0 +1,70 @@
+package cn.iocoder.yudao.module.iot.service.plugininstance;
+
+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.plugininstance.vo.PluginInstancePageReqVO;
+import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstanceSaveReqVO;
+import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;
+import cn.iocoder.yudao.module.iot.dal.mysql.plugininstance.PluginInstanceMapper;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PLUGIN_INSTANCE_NOT_EXISTS;
+
+/**
+ * IoT 插件实例 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+public class PluginInstanceServiceImpl implements PluginInstanceService {
+
+ @Resource
+ private PluginInstanceMapper pluginInstanceMapper;
+
+ @Override
+ public Long createPluginInstance(PluginInstanceSaveReqVO createReqVO) {
+ // 插入
+ PluginInstanceDO pluginInstance = BeanUtils.toBean(createReqVO, PluginInstanceDO.class);
+ pluginInstanceMapper.insert(pluginInstance);
+ // 返回
+ return pluginInstance.getId();
+ }
+
+ @Override
+ public void updatePluginInstance(PluginInstanceSaveReqVO updateReqVO) {
+ // 校验存在
+ validatePluginInstanceExists(updateReqVO.getId());
+ // 更新
+ PluginInstanceDO updateObj = BeanUtils.toBean(updateReqVO, PluginInstanceDO.class);
+ pluginInstanceMapper.updateById(updateObj);
+ }
+
+ @Override
+ public void deletePluginInstance(Long id) {
+ // 校验存在
+ validatePluginInstanceExists(id);
+ // 删除
+ pluginInstanceMapper.deleteById(id);
+ }
+
+ private void validatePluginInstanceExists(Long id) {
+ if (pluginInstanceMapper.selectById(id) == null) {
+ throw exception(PLUGIN_INSTANCE_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public PluginInstanceDO getPluginInstance(Long id) {
+ return pluginInstanceMapper.selectById(id);
+ }
+
+ @Override
+ public PageResult getPluginInstancePage(PluginInstancePageReqVO pageReqVO) {
+ return pluginInstanceMapper.selectPage(pageReqVO);
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java
index bcbf13ef45..9a2e96f671 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java
@@ -45,6 +45,22 @@ public interface IotProductService {
*/
IotProductDO getProduct(Long id);
+ /**
+ * 校验产品存在
+ *
+ * @param id 编号
+ * @return 产品
+ */
+ IotProductDO validateProductExists(Long id);
+
+ /**
+ * 校验产品存在
+ *
+ * @param productKey 产品 key
+ * @return 产品
+ */
+ IotProductDO validateProductExists(String productKey);
+
/**
* 获得产品分页
*
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
index b1d2d666ba..e7c1443883 100644
--- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
@@ -71,16 +71,26 @@ public class IotProductServiceImpl implements IotProductService {
productMapper.deleteById(id);
}
- private IotProductDO validateProductExists(Long id) {
- IotProductDO iotProductDO = productMapper.selectById(id);
- if (iotProductDO == null) {
+ @Override
+ public IotProductDO validateProductExists(Long id) {
+ IotProductDO product = productMapper.selectById(id);
+ if (product == null) {
throw exception(PRODUCT_NOT_EXISTS);
}
- return iotProductDO;
+ return product;
}
- private void validateProductStatus(IotProductDO iotProductDO) {
- if (Objects.equals(iotProductDO.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) {
+ @Override
+ public IotProductDO validateProductExists(String productKey) {
+ IotProductDO product = productMapper.selectByProductKey(productKey);
+ if (product == null) {
+ throw exception(PRODUCT_NOT_EXISTS);
+ }
+ return product;
+ }
+
+ private void validateProductStatus(IotProductDO product) {
+ if (Objects.equals(product.getStatus(), IotProductStatusEnum.PUBLISHED.getStatus())) {
throw exception(PRODUCT_STATUS_NOT_DELETE);
}
}
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininfo/PluginInfoMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininfo/PluginInfoMapper.xml
new file mode 100644
index 0000000000..f24f7e14ce
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininfo/PluginInfoMapper.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml
new file mode 100644
index 0000000000..2d297c785b
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/yudao-module-iot/yudao-module-iot-plugin/pom.xml b/yudao-module-iot/yudao-module-iot-plugin/pom.xml
new file mode 100644
index 0000000000..49c3215810
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-plugin/pom.xml
@@ -0,0 +1,31 @@
+
+
+
+ yudao-module-iot
+ cn.iocoder.boot
+ ${revision}
+
+ 4.0.0
+ yudao-module-iot-plugin
+ jar
+
+ ${project.artifactId}
+
+ 物联网 模块 - 插件
+
+
+
+
+ cn.iocoder.boot
+ yudao-common
+
+
+
+ org.pf4j
+ pf4j-spring
+
+
+
+
diff --git a/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/package-info.java b/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/package-info.java
new file mode 100644
index 0000000000..567dcb038b
--- /dev/null
+++ b/yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * 占位
+ *
+ * TODO 芋艿:后续删除
+ */
+package cn.iocoder.yudao.module.iot.plugin;