Commit 555310de66d18250116f14d2cf93f42155cc3862

Authored by 安浩浩
1 parent 39ba4e72

【功能新增】IoT:增加插件管理功能,包含插件实例和类型的定义及相关配置

Showing 30 changed files with 1726 additions and 1 deletions
yudao-dependencies/pom.xml
@@ -66,6 +66,7 @@ @@ -66,6 +66,7 @@
66 <ip2region.version>2.7.0</ip2region.version> 66 <ip2region.version>2.7.0</ip2region.version>
67 <bizlog-sdk.version>3.0.6</bizlog-sdk.version> 67 <bizlog-sdk.version>3.0.6</bizlog-sdk.version>
68 <mqtt.version>1.2.5</mqtt.version> 68 <mqtt.version>1.2.5</mqtt.version>
  69 + <pf4j-spring.version>0.9.0</pf4j-spring.version>
69 <!-- 三方云服务相关 --> 70 <!-- 三方云服务相关 -->
70 <okio.version>3.5.0</okio.version> 71 <okio.version>3.5.0</okio.version>
71 <okhttp3.version>4.11.0</okhttp3.version> 72 <okhttp3.version>4.11.0</okhttp3.version>
@@ -605,6 +606,13 @@ @@ -605,6 +606,13 @@
605 <version>${mqtt.version}</version> 606 <version>${mqtt.version}</version>
606 </dependency> 607 </dependency>
607 608
  609 + <!-- PF4J -->
  610 + <dependency>
  611 + <groupId>org.pf4j</groupId>
  612 + <artifactId>pf4j-spring</artifactId>
  613 + <version>${pf4j-spring.version}</version>
  614 + </dependency>
  615 +
608 </dependencies> 616 </dependencies>
609 </dependencyManagement> 617 </dependencyManagement>
610 618
yudao-module-iot/pom.xml
@@ -10,6 +10,7 @@ @@ -10,6 +10,7 @@
10 <modules> 10 <modules>
11 <module>yudao-module-iot-api</module> 11 <module>yudao-module-iot-api</module>
12 <module>yudao-module-iot-biz</module> 12 <module>yudao-module-iot-biz</module>
  13 + <module>yudao-module-iot-plugin</module>
13 </modules> 14 </modules>
14 <modelVersion>4.0.0</modelVersion> 15 <modelVersion>4.0.0</modelVersion>
15 16
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java
@@ -36,4 +36,15 @@ public interface ErrorCodeConstants { @@ -36,4 +36,15 @@ public interface ErrorCodeConstants {
36 // ========== 设备分组 1-050-005-000 ========== 36 // ========== 设备分组 1-050-005-000 ==========
37 ErrorCode DEVICE_GROUP_NOT_EXISTS = new ErrorCode(1_050_005_000, "设备分组不存在"); 37 ErrorCode DEVICE_GROUP_NOT_EXISTS = new ErrorCode(1_050_005_000, "设备分组不存在");
38 ErrorCode DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS = new ErrorCode(1_050_005_001, "设备分组下存在设备,不允许删除"); 38 ErrorCode DEVICE_GROUP_DELETE_FAIL_DEVICE_EXISTS = new ErrorCode(1_050_005_001, "设备分组下存在设备,不允许删除");
39 -} 39 +
  40 + // ========== 插件信息 1-050-006-000 ==========
  41 + ErrorCode PLUGIN_INFO_NOT_EXISTS = new ErrorCode(1_050_006_000, "插件信息不存在");
  42 + ErrorCode PLUGIN_INSTALL_FAILED = new ErrorCode(1_050_006_001, "插件安装失败");
  43 + ErrorCode PLUGIN_INSTALL_FAILED_FILE_NAME_NOT_MATCH = new ErrorCode(1_050_006_002, "插件安装失败,文件名与原插件id不匹配");
  44 + ErrorCode PLUGIN_INFO_DELETE_FAILED_RUNNING = new ErrorCode(1_050_006_003, "请先停止插件");
  45 + ErrorCode PLUGIN_STATUS_INVALID = new ErrorCode(1_050_006_004, "插件状态无效");
  46 +
  47 + // ========== 插件实例 1-050-007-000 ==========
  48 + ErrorCode PLUGIN_INSTANCE_NOT_EXISTS = new ErrorCode(1_050_007_000, "插件实例不存在");
  49 +
  50 +}
40 \ No newline at end of file 51 \ No newline at end of file
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginDeployTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.enums.plugin;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import lombok.Getter;
  5 +
  6 +import java.util.Arrays;
  7 +
  8 +/**
  9 + * IoT 部署方式枚举
  10 + *
  11 + * @author haohao
  12 + */
  13 +@Getter
  14 +public enum IotPluginDeployTypeEnum implements IntArrayValuable {
  15 +
  16 + UPLOAD(0, "上传jar"),
  17 + ALONE(1, "独立运行");
  18 +
  19 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotPluginDeployTypeEnum::getDeployType).toArray();
  20 +
  21 + /**
  22 + * 部署方式
  23 + */
  24 + private final Integer deployType;
  25 +
  26 + /**
  27 + * 部署方式名
  28 + */
  29 + private final String name;
  30 +
  31 + IotPluginDeployTypeEnum(Integer deployType, String name) {
  32 + this.deployType = deployType;
  33 + this.name = name;
  34 + }
  35 +
  36 + public static IotPluginDeployTypeEnum fromDeployType(Integer deployType) {
  37 + for (IotPluginDeployTypeEnum value : values()) {
  38 + if (value.getDeployType().equals(deployType)) {
  39 + return value;
  40 + }
  41 + }
  42 + return null;
  43 + }
  44 +
  45 + public static boolean isValidDeployType(Integer deployType) {
  46 + return fromDeployType(deployType) != null;
  47 + }
  48 +
  49 + @Override
  50 + public int[] array() {
  51 + return ARRAYS;
  52 + }
  53 +}
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginStatusEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.enums.plugin;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import lombok.Getter;
  5 +
  6 +import java.util.Arrays;
  7 +
  8 +/**
  9 + * IoT 插件状态枚举
  10 + *
  11 + * @author haohao
  12 + */
  13 +@Getter
  14 +public enum IotPluginStatusEnum implements IntArrayValuable {
  15 +
  16 + STOPPED(0, "停止"),
  17 + RUNNING(1, "运行");
  18 +
  19 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotPluginStatusEnum::getStatus).toArray();
  20 +
  21 + /**
  22 + * 状态
  23 + */
  24 + private final Integer status;
  25 +
  26 + /**
  27 + * 状态名
  28 + */
  29 + private final String name;
  30 +
  31 + IotPluginStatusEnum(Integer status, String name) {
  32 + this.status = status;
  33 + this.name = name;
  34 + }
  35 +
  36 + public static IotPluginStatusEnum fromState(Integer state) {
  37 + for (IotPluginStatusEnum value : values()) {
  38 + if (value.getStatus().equals(state)) {
  39 + return value;
  40 + }
  41 + }
  42 + return null;
  43 + }
  44 +
  45 + public static boolean isValidState(Integer state) {
  46 + return fromState(state) != null;
  47 + }
  48 +
  49 + public static boolean contains(Integer status) {
  50 + return Arrays.stream(values()).anyMatch(e -> e.getStatus().equals(status));
  51 + }
  52 +
  53 + @Override
  54 + public int[] array() {
  55 + return new int[0];
  56 + }
  57 +}
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/plugin/IotPluginTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.enums.plugin;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import lombok.Getter;
  5 +
  6 +import java.util.Arrays;
  7 +
  8 +/**
  9 + * IoT 插件类型枚举
  10 + *
  11 + * @author haohao
  12 + */
  13 +@Getter
  14 +public enum IotPluginTypeEnum implements IntArrayValuable {
  15 +
  16 + NORMAL(0, "普通插件"),
  17 + DEVICE(1, "设备插件");
  18 +
  19 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotPluginTypeEnum::getType).toArray();
  20 +
  21 + /**
  22 + * 类型
  23 + */
  24 + private final Integer type;
  25 +
  26 + /**
  27 + * 类型名
  28 + */
  29 + private final String name;
  30 +
  31 + IotPluginTypeEnum(Integer type, String name) {
  32 + this.type = type;
  33 + this.name = name;
  34 + }
  35 +
  36 + public static IotPluginTypeEnum fromType(Integer type) {
  37 + for (IotPluginTypeEnum value : values()) {
  38 + if (value.getType().equals(type)) {
  39 + return value;
  40 + }
  41 + }
  42 + return null;
  43 + }
  44 +
  45 + public static boolean isValidType(Integer type) {
  46 + return fromType(type) != null;
  47 + }
  48 +
  49 + @Override
  50 + public int[] array() {
  51 + return ARRAYS;
  52 + }
  53 +}
yudao-module-iot/yudao-module-iot-biz/pom.xml
@@ -69,6 +69,12 @@ @@ -69,6 +69,12 @@
69 <groupId>org.eclipse.paho</groupId> 69 <groupId>org.eclipse.paho</groupId>
70 <artifactId>org.eclipse.paho.client.mqttv3</artifactId> 70 <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
71 </dependency> 71 </dependency>
  72 +
  73 + <!-- PF4J -->
  74 + <dependency>
  75 + <groupId>org.pf4j</groupId>
  76 + <artifactId>pf4j-spring</artifactId>
  77 + </dependency>
72 </dependencies> 78 </dependencies>
73 79
74 </project> 80 </project>
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginInfoController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.plugininfo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO;
  7 +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoRespVO;
  8 +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO;
  9 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
  10 +import cn.iocoder.yudao.module.iot.service.plugininfo.PluginInfoService;
  11 +import io.swagger.v3.oas.annotations.Operation;
  12 +import io.swagger.v3.oas.annotations.Parameter;
  13 +import io.swagger.v3.oas.annotations.tags.Tag;
  14 +import jakarta.annotation.Resource;
  15 +import jakarta.validation.Valid;
  16 +import org.springframework.security.access.prepost.PreAuthorize;
  17 +import org.springframework.validation.annotation.Validated;
  18 +import org.springframework.web.bind.annotation.*;
  19 +import org.springframework.web.multipart.MultipartFile;
  20 +
  21 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  22 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  23 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  24 +import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_IS_EMPTY;
  25 +
  26 +@Tag(name = "管理后台 - IoT 插件信息")
  27 +@RestController
  28 +@RequestMapping("/iot/plugin-info")
  29 +@Validated
  30 +public class PluginInfoController {
  31 +
  32 + @Resource
  33 + private PluginInfoService pluginInfoService;
  34 +
  35 + @PostMapping("/create")
  36 + @Operation(summary = "创建插件信息")
  37 + @PreAuthorize("@ss.hasPermission('iot:plugin-info:create')")
  38 + public CommonResult<Long> createPluginInfo(@Valid @RequestBody PluginInfoSaveReqVO createReqVO) {
  39 + return success(pluginInfoService.createPluginInfo(createReqVO));
  40 + }
  41 +
  42 + @PutMapping("/update")
  43 + @Operation(summary = "更新插件信息")
  44 + @PreAuthorize("@ss.hasPermission('iot:plugin-info:update')")
  45 + public CommonResult<Boolean> updatePluginInfo(@Valid @RequestBody PluginInfoSaveReqVO updateReqVO) {
  46 + pluginInfoService.updatePluginInfo(updateReqVO);
  47 + return success(true);
  48 + }
  49 +
  50 + @DeleteMapping("/delete")
  51 + @Operation(summary = "删除插件信息")
  52 + @Parameter(name = "id", description = "编号", required = true)
  53 + @PreAuthorize("@ss.hasPermission('iot:plugin-info:delete')")
  54 + public CommonResult<Boolean> deletePluginInfo(@RequestParam("id") Long id) {
  55 + pluginInfoService.deletePluginInfo(id);
  56 + return success(true);
  57 + }
  58 +
  59 + @GetMapping("/get")
  60 + @Operation(summary = "获得插件信息")
  61 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  62 + @PreAuthorize("@ss.hasPermission('iot:plugin-info:query')")
  63 + public CommonResult<PluginInfoRespVO> getPluginInfo(@RequestParam("id") Long id) {
  64 + PluginInfoDO pluginInfo = pluginInfoService.getPluginInfo(id);
  65 + return success(BeanUtils.toBean(pluginInfo, PluginInfoRespVO.class));
  66 + }
  67 +
  68 + @GetMapping("/page")
  69 + @Operation(summary = "获得插件信息分页")
  70 + @PreAuthorize("@ss.hasPermission('iot:plugin-info:query')")
  71 + public CommonResult<PageResult<PluginInfoRespVO>> getPluginInfoPage(@Valid PluginInfoPageReqVO pageReqVO) {
  72 + PageResult<PluginInfoDO> pageResult = pluginInfoService.getPluginInfoPage(pageReqVO);
  73 + return success(BeanUtils.toBean(pageResult, PluginInfoRespVO.class));
  74 + }
  75 +
  76 + @RequestMapping(value = "/update-jar",
  77 + method = {RequestMethod.POST, RequestMethod.PUT}) // 解决 uni-app 不支持 Put 上传文件的问题
  78 + @Operation(summary = "上传Jar包")
  79 + public CommonResult<Boolean> uploadJar(
  80 + @RequestParam("id") Long id,
  81 + @RequestParam("jar") MultipartFile file) throws Exception {
  82 + if (file.isEmpty()) {
  83 + throw exception(FILE_IS_EMPTY);
  84 + }
  85 + pluginInfoService.uploadJar(id, file);
  86 + return success(true);
  87 + }
  88 +
  89 + // 修改插件状态
  90 + @PutMapping("/update-status")
  91 + @Operation(summary = "修改插件状态")
  92 + @PreAuthorize("@ss.hasPermission('iot:plugin-info:update')")
  93 + public CommonResult<Boolean> updateUserStatus(@Valid @RequestBody PluginInfoSaveReqVO reqVO) {
  94 + pluginInfoService.updatePluginStatus(reqVO.getId(), reqVO.getStatus());
  95 + return success(true);
  96 + }
  97 +
  98 +}
0 \ No newline at end of file 99 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo;
  2 +
  3 +import lombok.*;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  6 +import org.springframework.format.annotation.DateTimeFormat;
  7 +import java.time.LocalDateTime;
  8 +
  9 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  10 +
  11 +@Schema(description = "管理后台 - IoT 插件信息分页 Request VO")
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +@ToString(callSuper = true)
  15 +public class PluginInfoPageReqVO extends PageParam {
  16 +
  17 + @Schema(description = "插件包id", example = "24627")
  18 + private String pluginId;
  19 +
  20 + @Schema(description = "插件名称", example = "赵六")
  21 + private String name;
  22 +
  23 + @Schema(description = "描述", example = "你猜")
  24 + private String description;
  25 +
  26 + @Schema(description = "部署方式", example = "2")
  27 + private Integer deployType;
  28 +
  29 + @Schema(description = "插件包文件名")
  30 + private String file;
  31 +
  32 + @Schema(description = "插件版本")
  33 + private String version;
  34 +
  35 + @Schema(description = "插件类型", example = "2")
  36 + private Integer type;
  37 +
  38 + @Schema(description = "设备插件协议类型")
  39 + private String protocol;
  40 +
  41 + @Schema(description = "状态")
  42 + private Integer status;
  43 +
  44 + @Schema(description = "插件配置项描述信息")
  45 + private String configSchema;
  46 +
  47 + @Schema(description = "插件配置信息")
  48 + private String config;
  49 +
  50 + @Schema(description = "插件脚本")
  51 + private String script;
  52 +
  53 + @Schema(description = "创建时间")
  54 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  55 + private LocalDateTime[] createTime;
  56 +
  57 +}
0 \ No newline at end of file 58 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +
  6 +import java.time.LocalDateTime;
  7 +import com.alibaba.excel.annotation.*;
  8 +
  9 +@Schema(description = "管理后台 - IoT 插件信息 Response VO")
  10 +@Data
  11 +@ExcelIgnoreUnannotated
  12 +public class PluginInfoRespVO {
  13 +
  14 + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546")
  15 + @ExcelProperty("主键ID")
  16 + private Long id;
  17 +
  18 + @Schema(description = "插件包id", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627")
  19 + @ExcelProperty("插件包id")
  20 + private String pluginId;
  21 +
  22 + @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
  23 + @ExcelProperty("插件名称")
  24 + private String name;
  25 +
  26 + @Schema(description = "描述", example = "你猜")
  27 + @ExcelProperty("描述")
  28 + private String description;
  29 +
  30 + @Schema(description = "部署方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  31 + @ExcelProperty("部署方式")
  32 + private Integer deployType;
  33 +
  34 + @Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED)
  35 + @ExcelProperty("插件包文件名")
  36 + private String file;
  37 +
  38 + @Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED)
  39 + @ExcelProperty("插件版本")
  40 + private String version;
  41 +
  42 + @Schema(description = "插件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  43 + @ExcelProperty("插件类型")
  44 + private Integer type;
  45 +
  46 + @Schema(description = "设备插件协议类型")
  47 + @ExcelProperty("设备插件协议类型")
  48 + private String protocol;
  49 +
  50 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED)
  51 + @ExcelProperty("状态")
  52 + private Integer status;
  53 +
  54 + @Schema(description = "插件配置项描述信息")
  55 + @ExcelProperty("插件配置项描述信息")
  56 + private String configSchema;
  57 +
  58 + @Schema(description = "插件配置信息")
  59 + @ExcelProperty("插件配置信息")
  60 + private String config;
  61 +
  62 + @Schema(description = "插件脚本")
  63 + @ExcelProperty("插件脚本")
  64 + private String script;
  65 +
  66 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  67 + @ExcelProperty("创建时间")
  68 + private LocalDateTime createTime;
  69 +
  70 +}
0 \ No newline at end of file 71 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/vo/PluginInfoSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +
  6 +@Schema(description = "管理后台 - IoT 插件信息新增/修改 Request VO")
  7 +@Data
  8 +public class PluginInfoSaveReqVO {
  9 +
  10 + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546")
  11 + private Long id;
  12 +
  13 + @Schema(description = "插件包id", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627")
  14 + private String pluginId;
  15 +
  16 + @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
  17 + private String name;
  18 +
  19 + @Schema(description = "描述", example = "你猜")
  20 + private String description;
  21 +
  22 + @Schema(description = "部署方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  23 + private Integer deployType;
  24 +
  25 + @Schema(description = "插件包文件名", requiredMode = Schema.RequiredMode.REQUIRED)
  26 + private String file;
  27 +
  28 + @Schema(description = "插件版本", requiredMode = Schema.RequiredMode.REQUIRED)
  29 + private String version;
  30 +
  31 + @Schema(description = "插件类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  32 + private Integer type;
  33 +
  34 + @Schema(description = "设备插件协议类型")
  35 + private String protocol;
  36 +
  37 + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED)
  38 + private Integer status;
  39 +
  40 + @Schema(description = "插件配置项描述信息")
  41 + private String configSchema;
  42 +
  43 + @Schema(description = "插件配置信息")
  44 + private String config;
  45 +
  46 + @Schema(description = "插件脚本")
  47 + private String script;
  48 +
  49 +}
0 \ No newline at end of file 50 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/PluginInstanceController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.plugininstance;
  2 +
  3 +import org.springframework.web.bind.annotation.*;
  4 +import jakarta.annotation.Resource;
  5 +import org.springframework.validation.annotation.Validated;
  6 +import org.springframework.security.access.prepost.PreAuthorize;
  7 +import io.swagger.v3.oas.annotations.tags.Tag;
  8 +import io.swagger.v3.oas.annotations.Parameter;
  9 +import io.swagger.v3.oas.annotations.Operation;
  10 +
  11 +import jakarta.validation.*;
  12 +import jakarta.servlet.http.*;
  13 +import java.util.*;
  14 +import java.io.IOException;
  15 +
  16 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  17 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  18 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  19 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  20 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  21 +
  22 +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  23 +
  24 +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
  25 +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
  26 +
  27 +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*;
  28 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;
  29 +import cn.iocoder.yudao.module.iot.service.plugininstance.PluginInstanceService;
  30 +
  31 +@Tag(name = "管理后台 - IoT 插件实例")
  32 +@RestController
  33 +@RequestMapping("/iot/plugin-instance")
  34 +@Validated
  35 +public class PluginInstanceController {
  36 +
  37 + @Resource
  38 + private PluginInstanceService pluginInstanceService;
  39 +
  40 + @PostMapping("/create")
  41 + @Operation(summary = "创建IoT 插件实例")
  42 + @PreAuthorize("@ss.hasPermission('iot:plugin-instance:create')")
  43 + public CommonResult<Long> createPluginInstance(@Valid @RequestBody PluginInstanceSaveReqVO createReqVO) {
  44 + return success(pluginInstanceService.createPluginInstance(createReqVO));
  45 + }
  46 +
  47 + @PutMapping("/update")
  48 + @Operation(summary = "更新IoT 插件实例")
  49 + @PreAuthorize("@ss.hasPermission('iot:plugin-instance:update')")
  50 + public CommonResult<Boolean> updatePluginInstance(@Valid @RequestBody PluginInstanceSaveReqVO updateReqVO) {
  51 + pluginInstanceService.updatePluginInstance(updateReqVO);
  52 + return success(true);
  53 + }
  54 +
  55 + @DeleteMapping("/delete")
  56 + @Operation(summary = "删除IoT 插件实例")
  57 + @Parameter(name = "id", description = "编号", required = true)
  58 + @PreAuthorize("@ss.hasPermission('iot:plugin-instance:delete')")
  59 + public CommonResult<Boolean> deletePluginInstance(@RequestParam("id") Long id) {
  60 + pluginInstanceService.deletePluginInstance(id);
  61 + return success(true);
  62 + }
  63 +
  64 + @GetMapping("/get")
  65 + @Operation(summary = "获得IoT 插件实例")
  66 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  67 + @PreAuthorize("@ss.hasPermission('iot:plugin-instance:query')")
  68 + public CommonResult<PluginInstanceRespVO> getPluginInstance(@RequestParam("id") Long id) {
  69 + PluginInstanceDO pluginInstance = pluginInstanceService.getPluginInstance(id);
  70 + return success(BeanUtils.toBean(pluginInstance, PluginInstanceRespVO.class));
  71 + }
  72 +
  73 + @GetMapping("/page")
  74 + @Operation(summary = "获得IoT 插件实例分页")
  75 + @PreAuthorize("@ss.hasPermission('iot:plugin-instance:query')")
  76 + public CommonResult<PageResult<PluginInstanceRespVO>> getPluginInstancePage(@Valid PluginInstancePageReqVO pageReqVO) {
  77 + PageResult<PluginInstanceDO> pageResult = pluginInstanceService.getPluginInstancePage(pageReqVO);
  78 + return success(BeanUtils.toBean(pageResult, PluginInstanceRespVO.class));
  79 + }
  80 +
  81 + @GetMapping("/export-excel")
  82 + @Operation(summary = "导出IoT 插件实例 Excel")
  83 + @PreAuthorize("@ss.hasPermission('iot:plugin-instance:export')")
  84 + @ApiAccessLog(operateType = EXPORT)
  85 + public void exportPluginInstanceExcel(@Valid PluginInstancePageReqVO pageReqVO,
  86 + HttpServletResponse response) throws IOException {
  87 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
  88 + List<PluginInstanceDO> list = pluginInstanceService.getPluginInstancePage(pageReqVO).getList();
  89 + // 导出 Excel
  90 + ExcelUtils.write(response, "IoT 插件实例.xls", "数据", PluginInstanceRespVO.class,
  91 + BeanUtils.toBean(list, PluginInstanceRespVO.class));
  92 + }
  93 +
  94 +}
0 \ No newline at end of file 95 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstancePageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo;
  2 +
  3 +import lombok.*;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  6 +import org.springframework.format.annotation.DateTimeFormat;
  7 +import java.time.LocalDateTime;
  8 +
  9 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  10 +
  11 +@Schema(description = "管理后台 - IoT 插件实例分页 Request VO")
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +@ToString(callSuper = true)
  15 +public class PluginInstancePageReqVO extends PageParam {
  16 +
  17 + @Schema(description = "插件主程序id", example = "23738")
  18 + private String mainId;
  19 +
  20 + @Schema(description = "插件id", example = "26498")
  21 + private Long pluginId;
  22 +
  23 + @Schema(description = "插件主程序所在ip")
  24 + private String ip;
  25 +
  26 + @Schema(description = "插件主程序端口")
  27 + private Integer port;
  28 +
  29 + @Schema(description = "心跳时间,心路时间超过30秒需要剔除")
  30 + private Long heartbeatAt;
  31 +
  32 + @Schema(description = "创建时间")
  33 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  34 + private LocalDateTime[] createTime;
  35 +
  36 +}
0 \ No newline at end of file 37 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +import java.util.*;
  6 +import org.springframework.format.annotation.DateTimeFormat;
  7 +import java.time.LocalDateTime;
  8 +import com.alibaba.excel.annotation.*;
  9 +
  10 +@Schema(description = "管理后台 - IoT 插件实例 Response VO")
  11 +@Data
  12 +@ExcelIgnoreUnannotated
  13 +public class PluginInstanceRespVO {
  14 +
  15 + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "23864")
  16 + @ExcelProperty("主键ID")
  17 + private Long id;
  18 +
  19 + @Schema(description = "插件主程序id", requiredMode = Schema.RequiredMode.REQUIRED, example = "23738")
  20 + @ExcelProperty("插件主程序id")
  21 + private String mainId;
  22 +
  23 + @Schema(description = "插件id", requiredMode = Schema.RequiredMode.REQUIRED, example = "26498")
  24 + @ExcelProperty("插件id")
  25 + private Long pluginId;
  26 +
  27 + @Schema(description = "插件主程序所在ip", requiredMode = Schema.RequiredMode.REQUIRED)
  28 + @ExcelProperty("插件主程序所在ip")
  29 + private String ip;
  30 +
  31 + @Schema(description = "插件主程序端口", requiredMode = Schema.RequiredMode.REQUIRED)
  32 + @ExcelProperty("插件主程序端口")
  33 + private Integer port;
  34 +
  35 + @Schema(description = "心跳时间,心路时间超过30秒需要剔除", requiredMode = Schema.RequiredMode.REQUIRED)
  36 + @ExcelProperty("心跳时间,心路时间超过30秒需要剔除")
  37 + private Long heartbeatAt;
  38 +
  39 + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
  40 + @ExcelProperty("创建时间")
  41 + private LocalDateTime createTime;
  42 +
  43 +}
0 \ No newline at end of file 44 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininstance/vo/PluginInstanceSaveReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +import java.util.*;
  6 +import jakarta.validation.constraints.*;
  7 +
  8 +@Schema(description = "管理后台 - IoT 插件实例新增/修改 Request VO")
  9 +@Data
  10 +public class PluginInstanceSaveReqVO {
  11 +
  12 + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "23864")
  13 + private Long id;
  14 +
  15 + @Schema(description = "插件主程序id", requiredMode = Schema.RequiredMode.REQUIRED, example = "23738")
  16 + @NotEmpty(message = "插件主程序id不能为空")
  17 + private String mainId;
  18 +
  19 + @Schema(description = "插件id", requiredMode = Schema.RequiredMode.REQUIRED, example = "26498")
  20 + @NotNull(message = "插件id不能为空")
  21 + private Long pluginId;
  22 +
  23 + @Schema(description = "插件主程序所在ip", requiredMode = Schema.RequiredMode.REQUIRED)
  24 + @NotEmpty(message = "插件主程序所在ip不能为空")
  25 + private String ip;
  26 +
  27 + @Schema(description = "插件主程序端口", requiredMode = Schema.RequiredMode.REQUIRED)
  28 + @NotNull(message = "插件主程序端口不能为空")
  29 + private Integer port;
  30 +
  31 + @Schema(description = "心跳时间,心路时间超过30秒需要剔除", requiredMode = Schema.RequiredMode.REQUIRED)
  32 + @NotNull(message = "心跳时间,心路时间超过30秒需要剔除不能为空")
  33 + private Long heartbeatAt;
  34 +
  35 +}
0 \ No newline at end of file 36 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import com.baomidou.mybatisplus.annotation.KeySequence;
  5 +import com.baomidou.mybatisplus.annotation.TableId;
  6 +import com.baomidou.mybatisplus.annotation.TableName;
  7 +import lombok.*;
  8 +
  9 +/**
  10 + * IoT 插件信息 DO
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +@TableName("iot_plugin_info")
  15 +@KeySequence("iot_plugin_info_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  16 +@Data
  17 +@EqualsAndHashCode(callSuper = true)
  18 +@ToString(callSuper = true)
  19 +@Builder
  20 +@NoArgsConstructor
  21 +@AllArgsConstructor
  22 +public class PluginInfoDO extends BaseDO {
  23 +
  24 + /**
  25 + * 主键ID
  26 + */
  27 + @TableId
  28 + private Long id;
  29 + /**
  30 + * 插件包id
  31 + */
  32 + private String pluginId;
  33 + /**
  34 + * 插件名称
  35 + */
  36 + private String name;
  37 + /**
  38 + * 描述
  39 + */
  40 + private String description;
  41 + /**
  42 + * 部署方式
  43 + */
  44 + private Integer deployType;
  45 + /**
  46 + * 插件包文件名
  47 + */
  48 + private String file;
  49 + /**
  50 + * 插件版本
  51 + */
  52 + private String version;
  53 + /**
  54 + * 插件类型
  55 + */
  56 + private Integer type;
  57 + /**
  58 + * 设备插件协议类型
  59 + */
  60 + private String protocol;
  61 + /**
  62 + * 状态
  63 + */
  64 + private Integer status;
  65 + /**
  66 + * 插件配置项描述信息
  67 + */
  68 + private String configSchema;
  69 + /**
  70 + * 插件配置信息
  71 + */
  72 + private String config;
  73 + /**
  74 + * 插件脚本
  75 + */
  76 + private String script;
  77 +
  78 +}
0 \ No newline at end of file 79 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import java.time.LocalDateTime;
  6 +import java.time.LocalDateTime;
  7 +import com.baomidou.mybatisplus.annotation.*;
  8 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  9 +
  10 +/**
  11 + * IoT 插件实例 DO
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@TableName("iot_plugin_instance")
  16 +@KeySequence("iot_plugin_instance_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  17 +@Data
  18 +@EqualsAndHashCode(callSuper = true)
  19 +@ToString(callSuper = true)
  20 +@Builder
  21 +@NoArgsConstructor
  22 +@AllArgsConstructor
  23 +public class PluginInstanceDO extends BaseDO {
  24 +
  25 + /**
  26 + * 主键ID
  27 + */
  28 + @TableId
  29 + private Long id;
  30 + /**
  31 + * 插件主程序id
  32 + */
  33 + private String mainId;
  34 + /**
  35 + * 插件id
  36 + */
  37 + private Long pluginId;
  38 + /**
  39 + * 插件主程序所在ip
  40 + */
  41 + private String ip;
  42 + /**
  43 + * 插件主程序端口
  44 + */
  45 + private Integer port;
  46 + /**
  47 + * 心跳时间,心路时间超过30秒需要剔除
  48 + */
  49 + private Long heartbeatAt;
  50 +
  51 +}
0 \ No newline at end of file 52 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininfo/PluginInfoMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.mysql.plugininfo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
  7 +import org.apache.ibatis.annotations.Mapper;
  8 +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.*;
  9 +
  10 +/**
  11 + * IoT 插件信息 Mapper
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Mapper
  16 +public interface PluginInfoMapper extends BaseMapperX<PluginInfoDO> {
  17 +
  18 + default PageResult<PluginInfoDO> selectPage(PluginInfoPageReqVO reqVO) {
  19 + return selectPage(reqVO, new LambdaQueryWrapperX<PluginInfoDO>()
  20 + .eqIfPresent(PluginInfoDO::getPluginId, reqVO.getPluginId())
  21 + .likeIfPresent(PluginInfoDO::getName, reqVO.getName())
  22 + .eqIfPresent(PluginInfoDO::getDescription, reqVO.getDescription())
  23 + .eqIfPresent(PluginInfoDO::getDeployType, reqVO.getDeployType())
  24 + .eqIfPresent(PluginInfoDO::getFile, reqVO.getFile())
  25 + .eqIfPresent(PluginInfoDO::getVersion, reqVO.getVersion())
  26 + .eqIfPresent(PluginInfoDO::getType, reqVO.getType())
  27 + .eqIfPresent(PluginInfoDO::getProtocol, reqVO.getProtocol())
  28 + .eqIfPresent(PluginInfoDO::getStatus, reqVO.getStatus())
  29 + .eqIfPresent(PluginInfoDO::getConfigSchema, reqVO.getConfigSchema())
  30 + .eqIfPresent(PluginInfoDO::getConfig, reqVO.getConfig())
  31 + .eqIfPresent(PluginInfoDO::getScript, reqVO.getScript())
  32 + .betweenIfPresent(PluginInfoDO::getCreateTime, reqVO.getCreateTime())
  33 + .orderByDesc(PluginInfoDO::getId));
  34 + }
  35 +
  36 +}
0 \ No newline at end of file 37 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugininstance/PluginInstanceMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.dal.mysql.plugininstance;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;
  7 +import org.apache.ibatis.annotations.Mapper;
  8 +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*;
  9 +
  10 +/**
  11 + * IoT 插件实例 Mapper
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Mapper
  16 +public interface PluginInstanceMapper extends BaseMapperX<PluginInstanceDO> {
  17 +
  18 + default PageResult<PluginInstanceDO> selectPage(PluginInstancePageReqVO reqVO) {
  19 + return selectPage(reqVO, new LambdaQueryWrapperX<PluginInstanceDO>()
  20 + .eqIfPresent(PluginInstanceDO::getMainId, reqVO.getMainId())
  21 + .eqIfPresent(PluginInstanceDO::getPluginId, reqVO.getPluginId())
  22 + .eqIfPresent(PluginInstanceDO::getIp, reqVO.getIp())
  23 + .eqIfPresent(PluginInstanceDO::getPort, reqVO.getPort())
  24 + .eqIfPresent(PluginInstanceDO::getHeartbeatAt, reqVO.getHeartbeatAt())
  25 + .betweenIfPresent(PluginInstanceDO::getCreateTime, reqVO.getCreateTime())
  26 + .orderByDesc(PluginInstanceDO::getId));
  27 + }
  28 +
  29 +}
0 \ No newline at end of file 30 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.framework.plugin;
  2 +
  3 +import org.pf4j.spring.SpringPluginManager;
  4 +import org.springframework.context.annotation.Bean;
  5 +import org.springframework.context.annotation.Configuration;
  6 +
  7 +@Configuration
  8 +public class SpringConfiguration {
  9 +
  10 + @Bean
  11 + public SpringPluginManager pluginManager() {
  12 + return new SpringPluginManager();
  13 + }
  14 +
  15 +}
0 \ No newline at end of file 16 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.plugininfo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
  7 +import jakarta.validation.Valid;
  8 +import org.springframework.web.multipart.MultipartFile;
  9 +
  10 +import java.io.InputStream;
  11 +
  12 +/**
  13 + * IoT 插件信息 Service 接口
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +public interface PluginInfoService {
  18 +
  19 + /**
  20 + * 创建IoT 插件信息
  21 + *
  22 + * @param createReqVO 创建信息
  23 + * @return 编号
  24 + */
  25 + Long createPluginInfo(@Valid PluginInfoSaveReqVO createReqVO);
  26 +
  27 + /**
  28 + * 更新IoT 插件信息
  29 + *
  30 + * @param updateReqVO 更新信息
  31 + */
  32 + void updatePluginInfo(@Valid PluginInfoSaveReqVO updateReqVO);
  33 +
  34 + /**
  35 + * 删除IoT 插件信息
  36 + *
  37 + * @param id 编号
  38 + */
  39 + void deletePluginInfo(Long id);
  40 +
  41 + /**
  42 + * 获得IoT 插件信息
  43 + *
  44 + * @param id 编号
  45 + * @return IoT 插件信息
  46 + */
  47 + PluginInfoDO getPluginInfo(Long id);
  48 +
  49 + /**
  50 + * 获得IoT 插件信息分页
  51 + *
  52 + * @param pageReqVO 分页查询
  53 + * @return IoT 插件信息分页
  54 + */
  55 + PageResult<PluginInfoDO> getPluginInfoPage(PluginInfoPageReqVO pageReqVO);
  56 +
  57 + /**
  58 + * 上传插件的 JAR 包
  59 + *
  60 + * @param id 插件id
  61 + * @param file 文件
  62 + */
  63 + void uploadJar(Long id, MultipartFile file);
  64 +
  65 + /**
  66 + * 更新插件的状态
  67 + *
  68 + * @param id 插件id
  69 + * @param status 状态
  70 + */
  71 + void updatePluginStatus(Long id, Integer status);
  72 +}
0 \ No newline at end of file 73 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.plugininfo;
  2 +
  3 +import cn.hutool.core.io.IoUtil;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  6 +import cn.iocoder.yudao.module.infra.api.file.FileApi;
  7 +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoPageReqVO;
  8 +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.PluginInfoSaveReqVO;
  9 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
  10 +import cn.iocoder.yudao.module.iot.dal.mysql.plugininfo.PluginInfoMapper;
  11 +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
  12 +import jakarta.annotation.PostConstruct;
  13 +import jakarta.annotation.Resource;
  14 +import lombok.SneakyThrows;
  15 +import lombok.extern.slf4j.Slf4j;
  16 +import org.pf4j.PluginDescriptor;
  17 +import org.pf4j.PluginState;
  18 +import org.pf4j.PluginWrapper;
  19 +import org.pf4j.spring.SpringPluginManager;
  20 +import org.springframework.stereotype.Service;
  21 +import org.springframework.validation.annotation.Validated;
  22 +import org.springframework.web.multipart.MultipartFile;
  23 +
  24 +import java.nio.file.Path;
  25 +import java.util.concurrent.Executors;
  26 +import java.util.concurrent.TimeUnit;
  27 +
  28 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  29 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
  30 +
  31 +/**
  32 + * IoT 插件信息 Service 实现类
  33 + *
  34 + * @author 芋道源码
  35 + */
  36 +@Service
  37 +@Validated
  38 +@Slf4j
  39 +public class PluginInfoServiceImpl implements PluginInfoService {
  40 +
  41 + @Resource
  42 + private PluginInfoMapper pluginInfoMapper;
  43 +
  44 + @Resource
  45 + private SpringPluginManager pluginManager;
  46 +
  47 + @Resource
  48 + private FileApi fileApi;
  49 +
  50 + @Override
  51 + public Long createPluginInfo(PluginInfoSaveReqVO createReqVO) {
  52 + // 插入
  53 + PluginInfoDO pluginInfo = BeanUtils.toBean(createReqVO, PluginInfoDO.class);
  54 + pluginInfoMapper.insert(pluginInfo);
  55 + // 返回
  56 + return pluginInfo.getId();
  57 + }
  58 +
  59 + @Override
  60 + public void updatePluginInfo(PluginInfoSaveReqVO updateReqVO) {
  61 + // 校验存在
  62 + validatePluginInfoExists(updateReqVO.getId());
  63 + // 更新
  64 + PluginInfoDO updateObj = BeanUtils.toBean(updateReqVO, PluginInfoDO.class);
  65 + pluginInfoMapper.updateById(updateObj);
  66 + }
  67 +
  68 + @Override
  69 + public void deletePluginInfo(Long id) {
  70 + // 校验存在
  71 + PluginInfoDO pluginInfoDO = validatePluginInfoExists(id);
  72 +
  73 + // 停止插件
  74 + if (IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) {
  75 + throw exception(PLUGIN_INFO_DELETE_FAILED_RUNNING);
  76 + }
  77 +
  78 + // 卸载插件
  79 + PluginWrapper plugin = pluginManager.getPlugin(pluginInfoDO.getPluginId());
  80 + if (plugin != null) {
  81 + // 查询插件是否是启动状态
  82 + if (plugin.getPluginState().equals(PluginState.STARTED)) {
  83 + // 停止插件
  84 + pluginManager.stopPlugin(plugin.getPluginId());
  85 + }
  86 + // 卸载插件
  87 + pluginManager.unloadPlugin(plugin.getPluginId());
  88 + }
  89 +
  90 + // 删除
  91 + pluginInfoMapper.deleteById(id);
  92 + }
  93 +
  94 + private PluginInfoDO validatePluginInfoExists(Long id) {
  95 + PluginInfoDO pluginInfo = pluginInfoMapper.selectById(id);
  96 + if (pluginInfo == null) {
  97 + throw exception(PLUGIN_INFO_NOT_EXISTS);
  98 + }
  99 + return pluginInfo;
  100 + }
  101 +
  102 + @Override
  103 + public PluginInfoDO getPluginInfo(Long id) {
  104 + return pluginInfoMapper.selectById(id);
  105 + }
  106 +
  107 + @Override
  108 + public PageResult<PluginInfoDO> getPluginInfoPage(PluginInfoPageReqVO pageReqVO) {
  109 + return pluginInfoMapper.selectPage(pageReqVO);
  110 + }
  111 +
  112 + @Override
  113 + public void uploadJar(Long id, MultipartFile file) {
  114 + // 1. 校验存在
  115 + PluginInfoDO pluginInfoDo = validatePluginInfoExists(id);
  116 +
  117 + // 2. 判断文件名称与插件 ID 是否匹配
  118 + String pluginId = pluginInfoDo.getPluginId();
  119 +
  120 + // 3. 停止卸载旧的插件
  121 + // 3.1. 获取插件信息
  122 + PluginWrapper plugin = pluginManager.getPlugin(pluginId);
  123 + if (plugin != null) {
  124 + // 3.2. 如果插件状态是启动的,停止插件
  125 + if (plugin.getPluginState().equals(PluginState.STARTED)) {
  126 + pluginManager.stopPlugin(pluginId);
  127 + }
  128 + // 3.3. 卸载插件
  129 + pluginManager.unloadPlugin(pluginId);
  130 + }
  131 +
  132 + // 4. 上传插件
  133 + String pluginIdNew;
  134 + try {
  135 + String path = fileApi.createFile(IoUtil.readBytes(file.getInputStream()));
  136 + Path pluginPath = Path.of(path);
  137 + pluginIdNew = pluginManager.loadPlugin(pluginPath);
  138 + } catch (Exception e) {
  139 + throw exception(PLUGIN_INSTALL_FAILED);
  140 + }
  141 +
  142 + PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginIdNew);
  143 + if (pluginWrapper == null) {
  144 + throw exception(PLUGIN_INSTALL_FAILED);
  145 + }
  146 +
  147 +
  148 + // 5. 读取配置文件和脚本
  149 + String configJson = "";
  150 + String script = "";
  151 +
  152 + pluginInfoDo.setPluginId(pluginIdNew);
  153 + pluginInfoDo.setStatus(IotPluginStatusEnum.STOPPED.getStatus());
  154 + pluginInfoDo.setFile(file.getOriginalFilename());
  155 + pluginInfoDo.setConfigSchema(configJson);
  156 + pluginInfoDo.setScript(script);
  157 +
  158 + PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor();
  159 + pluginInfoDo.setVersion(pluginDescriptor.getVersion());
  160 + pluginInfoDo.setDescription(pluginDescriptor.getPluginDescription());
  161 + pluginInfoMapper.updateById(pluginInfoDo);
  162 +
  163 +
  164 + // 5. 读取配置文件和脚本
  165 +// String configJson = "";
  166 + // String script = "";
  167 +// try (JarFile jarFile = new JarFile(pluginInfoUpdate.getPluginPath())) {
  168 +// // 5.1 获取config文件在jar包中的路径
  169 +// String configFile = "classes/config.json";
  170 +// JarEntry configEntry = jarFile.getJarEntry(configFile);
  171 +//
  172 +// if (configEntry != null) {
  173 +// // 5.2 读取配置文件
  174 +// configJson = IoUtil.read(jarFile.getInputStream(configEntry), Charset.defaultCharset());
  175 +// log.info("configJson:{}", configJson);
  176 +// }
  177 +//
  178 +// // 5.3 读取script.js脚本
  179 +// String scriptFile = "classes/script.js";
  180 +// JarEntry scriptEntity = jarFile.getJarEntry(scriptFile);
  181 +// if (scriptEntity != null) {
  182 +// // 5.4 读取脚本文件
  183 +// script = IoUtil.read(jarFile.getInputStream(scriptEntity), Charset.defaultCharset());
  184 +// log.info("script:{}", script);
  185 +// }
  186 +// } catch (Exception e) {
  187 +// throw exception(PLUGIN_INSTALL_FAILED);
  188 +// }
  189 +
  190 +
  191 +// PluginState pluginState = pluginInfoUpdate.getPluginState();
  192 +// if (pluginState == PluginState.STARTED) {
  193 +// pluginInfoDo.setStatus(IotPluginStatusEnum.RUNNING.getStatus());
  194 +// }
  195 +// pluginInfoDo.setPluginId(pluginInfoUpdate.getPluginId());
  196 +// pluginInfoDo.setFile(file.getOriginalFilename());
  197 +// pluginInfoDo.setConfigSchema(configJson);
  198 +// pluginInfoDo.setScript(script);
  199 +//
  200 +// PluginDescriptor pluginDescriptor = pluginInfoUpdate.getPluginDescriptor();
  201 +// pluginInfoDo.setVersion(pluginDescriptor.getPluginVersion());
  202 +// pluginInfoDo.setDescription(pluginDescriptor.getDescription());
  203 +// pluginInfoMapper.updateById(pluginInfoDo);
  204 + }
  205 +
  206 + @Override
  207 + public void updatePluginStatus(Long id, Integer status) {
  208 + // 1. 校验存在
  209 + PluginInfoDO pluginInfoDo = validatePluginInfoExists(id);
  210 +
  211 + // 插件状态无效
  212 + if (!IotPluginStatusEnum.contains(status)) {
  213 + throw exception(PLUGIN_STATUS_INVALID);
  214 + }
  215 +
  216 + // 插件包为空
  217 +// String pluginId = pluginInfoDo.getPluginId();
  218 +// if (StrUtil.isBlank(pluginId)) {
  219 +// throw exception(PLUGIN_INFO_NOT_EXISTS);
  220 +// }
  221 +// com.gitee.starblues.core.PluginInfo pluginInfo = pluginOperator.getPluginInfo(pluginId);
  222 +// if (pluginInfo != null) {
  223 +// if (pluginInfoDo.getStatus().equals(IotPluginStatusEnum.RUNNING.getStatus()) && pluginInfo.getPluginState() != PluginState.STARTED) {
  224 +// // 启动插件
  225 +// pluginOperator.start(pluginId);
  226 +// } else if (pluginInfoDo.getStatus().equals(IotPluginStatusEnum.STOPPED.getStatus()) && pluginInfo.getPluginState() == PluginState.STARTED) {
  227 +// // 停止插件
  228 +// pluginOperator.stop(pluginId);
  229 +// }
  230 +// } else {
  231 +// // 已经停止,未获取到插件
  232 +// if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) {
  233 +// throw exception(PLUGIN_STATUS_INVALID);
  234 +// }
  235 +// }
  236 + pluginInfoDo.setStatus(status);
  237 + pluginInfoMapper.updateById(pluginInfoDo);
  238 + }
  239 +
  240 + @PostConstruct
  241 + public void init() {
  242 + Executors.newSingleThreadScheduledExecutor().schedule(this::startPlugins, 3, TimeUnit.SECONDS);
  243 + }
  244 +
  245 + @SneakyThrows
  246 + private void startPlugins() {
  247 +// while (!pluginOperator.inited()) {
  248 +// Thread.sleep(1000L);
  249 +// }
  250 +
  251 + for (PluginInfoDO pluginInfoDO : pluginInfoMapper.selectList()) {
  252 + if (!IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) {
  253 + continue;
  254 + }
  255 + log.info("start plugin:{}", pluginInfoDO.getPluginId());
  256 + try {
  257 +// com.gitee.starblues.core.PluginInfo plugin = pluginOperator.getPluginInfo(pluginInfoDO.getPluginId());
  258 +// if (plugin != null) {
  259 +// pluginOperator.start(plugin.getPluginId());
  260 +// }
  261 + } catch (Exception e) {
  262 + log.error("start plugin error", e);
  263 + }
  264 + }
  265 + }
  266 +
  267 +}
0 \ No newline at end of file 268 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.plugininstance;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstancePageReqVO;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstanceSaveReqVO;
  6 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;
  7 +import jakarta.validation.Valid;
  8 +
  9 +/**
  10 + * IoT 插件实例 Service 接口
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +public interface PluginInstanceService {
  15 +
  16 + /**
  17 + * 创建IoT 插件实例
  18 + *
  19 + * @param createReqVO 创建信息
  20 + * @return 编号
  21 + */
  22 + Long createPluginInstance(@Valid PluginInstanceSaveReqVO createReqVO);
  23 +
  24 + /**
  25 + * 更新IoT 插件实例
  26 + *
  27 + * @param updateReqVO 更新信息
  28 + */
  29 + void updatePluginInstance(@Valid PluginInstanceSaveReqVO updateReqVO);
  30 +
  31 + /**
  32 + * 删除IoT 插件实例
  33 + *
  34 + * @param id 编号
  35 + */
  36 + void deletePluginInstance(Long id);
  37 +
  38 + /**
  39 + * 获得IoT 插件实例
  40 + *
  41 + * @param id 编号
  42 + * @return IoT 插件实例
  43 + */
  44 + PluginInstanceDO getPluginInstance(Long id);
  45 +
  46 + /**
  47 + * 获得IoT 插件实例分页
  48 + *
  49 + * @param pageReqVO 分页查询
  50 + * @return IoT 插件实例分页
  51 + */
  52 + PageResult<PluginInstanceDO> getPluginInstancePage(PluginInstancePageReqVO pageReqVO);
  53 +
  54 +}
0 \ No newline at end of file 55 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.plugininstance;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstancePageReqVO;
  6 +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.PluginInstanceSaveReqVO;
  7 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;
  8 +import cn.iocoder.yudao.module.iot.dal.mysql.plugininstance.PluginInstanceMapper;
  9 +import jakarta.annotation.Resource;
  10 +import org.springframework.stereotype.Service;
  11 +import org.springframework.validation.annotation.Validated;
  12 +
  13 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  14 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PLUGIN_INSTANCE_NOT_EXISTS;
  15 +
  16 +/**
  17 + * IoT 插件实例 Service 实现类
  18 + *
  19 + * @author 芋道源码
  20 + */
  21 +@Service
  22 +@Validated
  23 +public class PluginInstanceServiceImpl implements PluginInstanceService {
  24 +
  25 + @Resource
  26 + private PluginInstanceMapper pluginInstanceMapper;
  27 +
  28 + @Override
  29 + public Long createPluginInstance(PluginInstanceSaveReqVO createReqVO) {
  30 + // 插入
  31 + PluginInstanceDO pluginInstance = BeanUtils.toBean(createReqVO, PluginInstanceDO.class);
  32 + pluginInstanceMapper.insert(pluginInstance);
  33 + // 返回
  34 + return pluginInstance.getId();
  35 + }
  36 +
  37 + @Override
  38 + public void updatePluginInstance(PluginInstanceSaveReqVO updateReqVO) {
  39 + // 校验存在
  40 + validatePluginInstanceExists(updateReqVO.getId());
  41 + // 更新
  42 + PluginInstanceDO updateObj = BeanUtils.toBean(updateReqVO, PluginInstanceDO.class);
  43 + pluginInstanceMapper.updateById(updateObj);
  44 + }
  45 +
  46 + @Override
  47 + public void deletePluginInstance(Long id) {
  48 + // 校验存在
  49 + validatePluginInstanceExists(id);
  50 + // 删除
  51 + pluginInstanceMapper.deleteById(id);
  52 + }
  53 +
  54 + private void validatePluginInstanceExists(Long id) {
  55 + if (pluginInstanceMapper.selectById(id) == null) {
  56 + throw exception(PLUGIN_INSTANCE_NOT_EXISTS);
  57 + }
  58 + }
  59 +
  60 + @Override
  61 + public PluginInstanceDO getPluginInstance(Long id) {
  62 + return pluginInstanceMapper.selectById(id);
  63 + }
  64 +
  65 + @Override
  66 + public PageResult<PluginInstanceDO> getPluginInstancePage(PluginInstancePageReqVO pageReqVO) {
  67 + return pluginInstanceMapper.selectPage(pageReqVO);
  68 + }
  69 +
  70 +}
0 \ No newline at end of file 71 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininfo/PluginInfoMapper.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +<mapper namespace="cn.iocoder.yudao.module.iot.dal.mysql.plugininfo.PluginInfoMapper">
  4 +
  5 + <!--
  6 + 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
  7 + 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
  8 + 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
  9 + 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
  10 + -->
  11 +
  12 +</mapper>
0 \ No newline at end of file 13 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/plugininstance/PluginInstanceMapper.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +<mapper namespace="cn.iocoder.yudao.module.iot.dal.mysql.plugininstance.PluginInstanceMapper">
  4 +
  5 + <!--
  6 + 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
  7 + 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
  8 + 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
  9 + 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
  10 + -->
  11 +
  12 +</mapper>
0 \ No newline at end of file 13 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImplTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.plugininfo;
  2 +
  3 +import org.junit.jupiter.api.Disabled;
  4 +import org.junit.jupiter.api.Test;
  5 +
  6 +import jakarta.annotation.Resource;
  7 +
  8 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  9 +
  10 +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.vo.*;
  11 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
  12 +import cn.iocoder.yudao.module.iot.dal.mysql.plugininfo.PluginInfoMapper;
  13 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  14 +
  15 +import org.springframework.context.annotation.Import;
  16 +
  17 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
  18 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
  19 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  20 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
  21 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
  22 +import static org.junit.jupiter.api.Assertions.*;
  23 +
  24 +/**
  25 + * {@link PluginInfoServiceImpl} 的单元测试类
  26 + *
  27 + * @author 芋道源码
  28 + */
  29 +@Import(PluginInfoServiceImpl.class)
  30 +public class PluginInfoServiceImplTest extends BaseDbUnitTest {
  31 +
  32 + @Resource
  33 + private PluginInfoServiceImpl pluginInfoService;
  34 +
  35 + @Resource
  36 + private PluginInfoMapper pluginInfoMapper;
  37 +
  38 + @Test
  39 + public void testCreatePluginInfo_success() {
  40 + // 准备参数
  41 + PluginInfoSaveReqVO createReqVO = randomPojo(PluginInfoSaveReqVO.class).setId(null);
  42 +
  43 + // 调用
  44 + Long pluginInfoId = pluginInfoService.createPluginInfo(createReqVO);
  45 + // 断言
  46 + assertNotNull(pluginInfoId);
  47 + // 校验记录的属性是否正确
  48 + PluginInfoDO pluginInfo = pluginInfoMapper.selectById(pluginInfoId);
  49 + assertPojoEquals(createReqVO, pluginInfo, "id");
  50 + }
  51 +
  52 + @Test
  53 + public void testUpdatePluginInfo_success() {
  54 + // mock 数据
  55 + PluginInfoDO dbPluginInfo = randomPojo(PluginInfoDO.class);
  56 + pluginInfoMapper.insert(dbPluginInfo);// @Sql: 先插入出一条存在的数据
  57 + // 准备参数
  58 + PluginInfoSaveReqVO updateReqVO = randomPojo(PluginInfoSaveReqVO.class, o -> {
  59 + o.setId(dbPluginInfo.getId()); // 设置更新的 ID
  60 + });
  61 +
  62 + // 调用
  63 + pluginInfoService.updatePluginInfo(updateReqVO);
  64 + // 校验是否更新正确
  65 + PluginInfoDO pluginInfo = pluginInfoMapper.selectById(updateReqVO.getId()); // 获取最新的
  66 + assertPojoEquals(updateReqVO, pluginInfo);
  67 + }
  68 +
  69 + @Test
  70 + public void testUpdatePluginInfo_notExists() {
  71 + // 准备参数
  72 + PluginInfoSaveReqVO updateReqVO = randomPojo(PluginInfoSaveReqVO.class);
  73 +
  74 + // 调用, 并断言异常
  75 + assertServiceException(() -> pluginInfoService.updatePluginInfo(updateReqVO), PLUGIN_INFO_NOT_EXISTS);
  76 + }
  77 +
  78 + @Test
  79 + public void testDeletePluginInfo_success() {
  80 + // mock 数据
  81 + PluginInfoDO dbPluginInfo = randomPojo(PluginInfoDO.class);
  82 + pluginInfoMapper.insert(dbPluginInfo);// @Sql: 先插入出一条存在的数据
  83 + // 准备参数
  84 + Long id = dbPluginInfo.getId();
  85 +
  86 + // 调用
  87 + pluginInfoService.deletePluginInfo(id);
  88 + // 校验数据不存在了
  89 + assertNull(pluginInfoMapper.selectById(id));
  90 + }
  91 +
  92 + @Test
  93 + public void testDeletePluginInfo_notExists() {
  94 + // 准备参数
  95 + Long id = randomLongId();
  96 +
  97 + // 调用, 并断言异常
  98 + assertServiceException(() -> pluginInfoService.deletePluginInfo(id), PLUGIN_INFO_NOT_EXISTS);
  99 + }
  100 +
  101 + @Test
  102 + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
  103 + public void testGetPluginInfoPage() {
  104 + // mock 数据
  105 + PluginInfoDO dbPluginInfo = randomPojo(PluginInfoDO.class, o -> { // 等会查询到
  106 + o.setPluginId(null);
  107 + o.setName(null);
  108 + o.setDescription(null);
  109 + o.setDeployType(null);
  110 + o.setFile(null);
  111 + o.setVersion(null);
  112 + o.setType(null);
  113 + o.setProtocol(null);
  114 + o.setStatus(null);
  115 + o.setConfigSchema(null);
  116 + o.setConfig(null);
  117 + o.setScript(null);
  118 + o.setCreateTime(null);
  119 + });
  120 + pluginInfoMapper.insert(dbPluginInfo);
  121 + // 测试 pluginId 不匹配
  122 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setPluginId(null)));
  123 + // 测试 name 不匹配
  124 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setName(null)));
  125 + // 测试 description 不匹配
  126 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setDescription(null)));
  127 + // 测试 deployType 不匹配
  128 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setDeployType(null)));
  129 + // 测试 file 不匹配
  130 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setFile(null)));
  131 + // 测试 version 不匹配
  132 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setVersion(null)));
  133 + // 测试 type 不匹配
  134 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setType(null)));
  135 + // 测试 protocol 不匹配
  136 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setProtocol(null)));
  137 + // 测试 state 不匹配
  138 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setStatus(null)));
  139 + // 测试 configSchema 不匹配
  140 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setConfigSchema(null)));
  141 + // 测试 config 不匹配
  142 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setConfig(null)));
  143 + // 测试 script 不匹配
  144 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setScript(null)));
  145 + // 测试 createTime 不匹配
  146 + pluginInfoMapper.insert(cloneIgnoreId(dbPluginInfo, o -> o.setCreateTime(null)));
  147 + // 准备参数
  148 + PluginInfoPageReqVO reqVO = new PluginInfoPageReqVO();
  149 + reqVO.setPluginId(null);
  150 + reqVO.setName(null);
  151 + reqVO.setDescription(null);
  152 + reqVO.setDeployType(null);
  153 + reqVO.setFile(null);
  154 + reqVO.setVersion(null);
  155 + reqVO.setType(null);
  156 + reqVO.setProtocol(null);
  157 + reqVO.setStatus(null);
  158 + reqVO.setConfigSchema(null);
  159 + reqVO.setConfig(null);
  160 + reqVO.setScript(null);
  161 + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
  162 +
  163 + // 调用
  164 + PageResult<PluginInfoDO> pageResult = pluginInfoService.getPluginInfoPage(reqVO);
  165 + // 断言
  166 + assertEquals(1, pageResult.getTotal());
  167 + assertEquals(1, pageResult.getList().size());
  168 + assertPojoEquals(dbPluginInfo, pageResult.getList().get(0));
  169 + }
  170 +
  171 +}
0 \ No newline at end of file 172 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/plugininstance/PluginInstanceServiceImplTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.service.plugininstance;
  2 +
  3 +import org.junit.jupiter.api.Disabled;
  4 +import org.junit.jupiter.api.Test;
  5 +import org.springframework.boot.test.mock.mockito.MockBean;
  6 +
  7 +import jakarta.annotation.Resource;
  8 +
  9 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  10 +
  11 +import cn.iocoder.yudao.module.iot.controller.admin.plugininstance.vo.*;
  12 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;
  13 +import cn.iocoder.yudao.module.iot.dal.mysql.plugininstance.PluginInstanceMapper;
  14 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  15 +
  16 +import jakarta.annotation.Resource;
  17 +import org.springframework.context.annotation.Import;
  18 +import java.util.*;
  19 +import java.time.LocalDateTime;
  20 +
  21 +import static cn.hutool.core.util.RandomUtil.*;
  22 +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
  23 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
  24 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  25 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
  26 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
  27 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.*;
  28 +import static org.junit.jupiter.api.Assertions.*;
  29 +import static org.mockito.Mockito.*;
  30 +
  31 +/**
  32 + * {@link PluginInstanceServiceImpl} 的单元测试类
  33 + *
  34 + * @author 芋道源码
  35 + */
  36 +@Import(PluginInstanceServiceImpl.class)
  37 +public class PluginInstanceServiceImplTest extends BaseDbUnitTest {
  38 +
  39 + @Resource
  40 + private PluginInstanceServiceImpl pluginInstanceService;
  41 +
  42 + @Resource
  43 + private PluginInstanceMapper pluginInstanceMapper;
  44 +
  45 + @Test
  46 + public void testCreatePluginInstance_success() {
  47 + // 准备参数
  48 + PluginInstanceSaveReqVO createReqVO = randomPojo(PluginInstanceSaveReqVO.class).setId(null);
  49 +
  50 + // 调用
  51 + Long pluginInstanceId = pluginInstanceService.createPluginInstance(createReqVO);
  52 + // 断言
  53 + assertNotNull(pluginInstanceId);
  54 + // 校验记录的属性是否正确
  55 + PluginInstanceDO pluginInstance = pluginInstanceMapper.selectById(pluginInstanceId);
  56 + assertPojoEquals(createReqVO, pluginInstance, "id");
  57 + }
  58 +
  59 + @Test
  60 + public void testUpdatePluginInstance_success() {
  61 + // mock 数据
  62 + PluginInstanceDO dbPluginInstance = randomPojo(PluginInstanceDO.class);
  63 + pluginInstanceMapper.insert(dbPluginInstance);// @Sql: 先插入出一条存在的数据
  64 + // 准备参数
  65 + PluginInstanceSaveReqVO updateReqVO = randomPojo(PluginInstanceSaveReqVO.class, o -> {
  66 + o.setId(dbPluginInstance.getId()); // 设置更新的 ID
  67 + });
  68 +
  69 + // 调用
  70 + pluginInstanceService.updatePluginInstance(updateReqVO);
  71 + // 校验是否更新正确
  72 + PluginInstanceDO pluginInstance = pluginInstanceMapper.selectById(updateReqVO.getId()); // 获取最新的
  73 + assertPojoEquals(updateReqVO, pluginInstance);
  74 + }
  75 +
  76 + @Test
  77 + public void testUpdatePluginInstance_notExists() {
  78 + // 准备参数
  79 + PluginInstanceSaveReqVO updateReqVO = randomPojo(PluginInstanceSaveReqVO.class);
  80 +
  81 + // 调用, 并断言异常
  82 + assertServiceException(() -> pluginInstanceService.updatePluginInstance(updateReqVO), PLUGIN_INSTANCE_NOT_EXISTS);
  83 + }
  84 +
  85 + @Test
  86 + public void testDeletePluginInstance_success() {
  87 + // mock 数据
  88 + PluginInstanceDO dbPluginInstance = randomPojo(PluginInstanceDO.class);
  89 + pluginInstanceMapper.insert(dbPluginInstance);// @Sql: 先插入出一条存在的数据
  90 + // 准备参数
  91 + Long id = dbPluginInstance.getId();
  92 +
  93 + // 调用
  94 + pluginInstanceService.deletePluginInstance(id);
  95 + // 校验数据不存在了
  96 + assertNull(pluginInstanceMapper.selectById(id));
  97 + }
  98 +
  99 + @Test
  100 + public void testDeletePluginInstance_notExists() {
  101 + // 准备参数
  102 + Long id = randomLongId();
  103 +
  104 + // 调用, 并断言异常
  105 + assertServiceException(() -> pluginInstanceService.deletePluginInstance(id), PLUGIN_INSTANCE_NOT_EXISTS);
  106 + }
  107 +
  108 + @Test
  109 + @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
  110 + public void testGetPluginInstancePage() {
  111 + // mock 数据
  112 + PluginInstanceDO dbPluginInstance = randomPojo(PluginInstanceDO.class, o -> { // 等会查询到
  113 + o.setMainId(null);
  114 + o.setPluginId(null);
  115 + o.setIp(null);
  116 + o.setPort(null);
  117 + o.setHeartbeatAt(null);
  118 + o.setCreateTime(null);
  119 + });
  120 + pluginInstanceMapper.insert(dbPluginInstance);
  121 + // 测试 mainId 不匹配
  122 + pluginInstanceMapper.insert(cloneIgnoreId(dbPluginInstance, o -> o.setMainId(null)));
  123 + // 测试 pluginId 不匹配
  124 + pluginInstanceMapper.insert(cloneIgnoreId(dbPluginInstance, o -> o.setPluginId(null)));
  125 + // 测试 ip 不匹配
  126 + pluginInstanceMapper.insert(cloneIgnoreId(dbPluginInstance, o -> o.setIp(null)));
  127 + // 测试 port 不匹配
  128 + pluginInstanceMapper.insert(cloneIgnoreId(dbPluginInstance, o -> o.setPort(null)));
  129 + // 测试 heartbeatAt 不匹配
  130 + pluginInstanceMapper.insert(cloneIgnoreId(dbPluginInstance, o -> o.setHeartbeatAt(null)));
  131 + // 测试 createTime 不匹配
  132 + pluginInstanceMapper.insert(cloneIgnoreId(dbPluginInstance, o -> o.setCreateTime(null)));
  133 + // 准备参数
  134 + PluginInstancePageReqVO reqVO = new PluginInstancePageReqVO();
  135 + reqVO.setMainId(null);
  136 + reqVO.setPluginId(null);
  137 + reqVO.setIp(null);
  138 + reqVO.setPort(null);
  139 + reqVO.setHeartbeatAt(null);
  140 + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
  141 +
  142 + // 调用
  143 + PageResult<PluginInstanceDO> pageResult = pluginInstanceService.getPluginInstancePage(reqVO);
  144 + // 断言
  145 + assertEquals(1, pageResult.getTotal());
  146 + assertEquals(1, pageResult.getList().size());
  147 + assertPojoEquals(dbPluginInstance, pageResult.getList().get(0));
  148 + }
  149 +
  150 +}
0 \ No newline at end of file 151 \ No newline at end of file
yudao-module-iot/yudao-module-iot-plugin/pom.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <parent>
  6 + <artifactId>yudao-module-iot</artifactId>
  7 + <groupId>cn.iocoder.boot</groupId>
  8 + <version>${revision}</version>
  9 + </parent>
  10 + <modelVersion>4.0.0</modelVersion>
  11 + <artifactId>yudao-module-iot-plugin</artifactId>
  12 + <packaging>jar</packaging>
  13 +
  14 + <name>${project.artifactId}</name>
  15 + <description>
  16 + 物联网 模块 - 插件
  17 + </description>
  18 +
  19 + <dependencies>
  20 + <dependency>
  21 + <groupId>cn.iocoder.boot</groupId>
  22 + <artifactId>yudao-common</artifactId>
  23 + </dependency>
  24 +
  25 + <dependency>
  26 + <groupId>org.pf4j</groupId>
  27 + <artifactId>pf4j-spring</artifactId>
  28 + </dependency>
  29 + </dependencies>
  30 +
  31 +</project>
yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/package-info.java 0 → 100644
  1 +/**
  2 + * 占位
  3 + *
  4 + * TODO 芋艿:后续删除
  5 + */
  6 +package cn.iocoder.yudao.module.iot.plugin;