فهرست منبع

【代码评审】IoT:plugin 相关的实现

YunaiV 6 ماه پیش
والد
کامیت
b46e630912
11فایلهای تغییر یافته به همراه119 افزوده شده و 110 حذف شده
  1. 1 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInfoController.java
  2. 1 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/info/PluginInfoSaveReqVO.java
  3. 5 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInfoDO.java
  4. 18 8
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/config/IotPluginConfiguration.java
  5. 0 25
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/CustomPluginStateListener.java
  6. 52 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStartRunner.java
  7. 21 0
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStateListener.java
  8. 0 49
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/PluginStart.java
  9. 1 1
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoService.java
  10. 17 23
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoServiceImpl.java
  11. 3 2
      yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java

+ 1 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInfoController.java

@@ -78,6 +78,7 @@ public class PluginInfoController {
         return success(true);
     }
 
+    // TODO @haohao:要不独立一个 VO,不用 PluginInfoSaveReqVO
     @PutMapping("/update-status")
     @Operation(summary = "修改插件状态")
     @PreAuthorize("@ss.hasPermission('iot:plugin-info:update')")

+ 1 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/info/PluginInfoSaveReqVO.java

@@ -13,7 +13,7 @@ public class PluginInfoSaveReqVO {
 
     // TODO @haohao:一些枚举字段,需要加枚举校验。例如说,deployType、status、type 等
 
-    @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546")
+    @Schema(description = "主键编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546")
     private Long id;
 
     @Schema(description = "插件包标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627")

+ 5 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugin/IotPluginInfoDO.java

@@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.*;
 
+// TODO @haohao:建议 IotPluginInfoDO 改成 IotPluginConfigDO,插件配置。项目里,暂时没有用 info 作为配置的哈。
 /**
  * IoT 插件信息 DO
  *
@@ -38,7 +39,7 @@ public class IotPluginInfoDO extends BaseDO {
      */
     private String name;
     /**
-     * 描述
+     * 插件描述
      */
     private String description;
     /**
@@ -68,12 +69,14 @@ public class IotPluginInfoDO extends BaseDO {
      */
     // TODO @芋艿:枚举字段
     private String protocol;
+    // TODO @haohao:这个字段,是不是直接用 CommonStatus,开启、禁用;然后插件实例那,online 是否在线
     /**
      * 状态
      * <p>
      * 枚举 {@link IotPluginStatusEnum}
      */
     private Integer status;
+
     // TODO @芋艿:configSchema、config 示例字段
     /**
      * 插件配置项描述信息
@@ -83,6 +86,7 @@ public class IotPluginInfoDO extends BaseDO {
      * 插件配置信息
      */
     private String config;
+
     // TODO @芋艿:script 后续的使用
     /**
      * 插件脚本

+ 18 - 8
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/config/UnifiedConfiguration.java → yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/config/IotPluginConfiguration.java

@@ -1,6 +1,8 @@
 package cn.iocoder.yudao.module.iot.framework.plugin.config;
 
-import cn.iocoder.yudao.module.iot.framework.plugin.core.CustomPluginStateListener;
+import cn.iocoder.yudao.module.iot.framework.plugin.core.IotPluginStartRunner;
+import cn.iocoder.yudao.module.iot.framework.plugin.core.IotPluginStateListener;
+import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInfoService;
 import lombok.extern.slf4j.Slf4j;
 import org.pf4j.spring.SpringPluginManager;
 import org.springframework.beans.factory.annotation.Value;
@@ -9,16 +11,24 @@ import org.springframework.context.annotation.Configuration;
 
 import java.nio.file.Paths;
 
-// TODO @芋艿:需要 review 下
-@Slf4j
+/**
+ * IoT 插件配置类
+ *
+ * @author haohao
+ */
 @Configuration
-public class UnifiedConfiguration {
+@Slf4j
+public class IotPluginConfiguration {
 
-    @Value("${pf4j.pluginsDir:pluginsDir}")
-    private String pluginsDir;
+    @Bean
+    public IotPluginStartRunner pluginStartRunner(SpringPluginManager pluginManager,
+                                                  IotPluginInfoService pluginInfoService) {
+        return new IotPluginStartRunner(pluginManager, pluginInfoService);
+    }
 
+    // TODO @芋艿:需要 review 下
     @Bean
-    public SpringPluginManager pluginManager() {
+    public SpringPluginManager pluginManager(@Value("${pf4j.pluginsDir:pluginsDir}") String pluginsDir) {
         log.info("[init][实例化 SpringPluginManager]");
         SpringPluginManager springPluginManager = new SpringPluginManager(Paths.get(pluginsDir)) {
 //        SpringPluginManager springPluginManager = new SpringPluginManager() {
@@ -30,7 +40,7 @@ public class UnifiedConfiguration {
             }
 
         };
-        springPluginManager.addPluginStateListener(new CustomPluginStateListener());
+        springPluginManager.addPluginStateListener(new IotPluginStateListener());
         return springPluginManager;
     }
 

+ 0 - 25
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/CustomPluginStateListener.java

@@ -1,25 +0,0 @@
-package cn.iocoder.yudao.module.iot.framework.plugin.core;
-
-import lombok.extern.slf4j.Slf4j;
-import org.pf4j.PluginStateEvent;
-import org.pf4j.PluginStateListener;
-import org.springframework.stereotype.Component;
-
-// TODO @芋艿:需要 review 下
-@Component
-@Slf4j
-public class CustomPluginStateListener implements PluginStateListener {
-
-    @Override
-    public void pluginStateChanged(PluginStateEvent event) {
-        // 1. 获取插件ID
-        String pluginId = event.getPlugin().getPluginId();
-        // 2. 获取插件旧状态
-        String oldState = event.getOldState().toString();
-        // 3. 获取插件新状态
-        String newState = event.getPluginState().toString();
-        // 4. 打印日志信息
-        log.info("插件的状态 '{}' 已更改为 '{}' 至 '{}'", pluginId, oldState, newState);
-    }
-
-}

+ 52 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStartRunner.java

@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.module.iot.framework.plugin.core;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
+import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInfoDO;
+import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginDeployTypeEnum;
+import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
+import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInfoService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.pf4j.spring.SpringPluginManager;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+
+import java.util.List;
+
+/**
+ * IoT 插件启动 Runner
+ *
+ * 用于 Spring Boot 启动时,启动 {@link IotPluginDeployTypeEnum#JAR} 部署类型的插件
+ */
+@RequiredArgsConstructor
+@Slf4j
+public class IotPluginStartRunner implements ApplicationRunner {
+
+    private final SpringPluginManager springPluginManager;
+
+    private final IotPluginInfoService pluginInfoService;
+
+    @Override
+    public void run(ApplicationArguments args) {
+        List<IotPluginInfoDO> pluginInfoList = TenantUtils.executeIgnore(
+                // TODO @haohao:需要查询部署类型哈
+                () -> pluginInfoService.getPluginInfoListByStatus(IotPluginStatusEnum.RUNNING.getStatus()));
+        if (CollUtil.isEmpty(pluginInfoList)) {
+            log.info("[run][没有需要启动的插件]");
+            return;
+        }
+
+        // 遍历插件列表,逐个启动
+        pluginInfoList.forEach(pluginInfo -> {
+            try {
+                log.info("[run][插件({}) 启动开始]", pluginInfo.getPluginKey());
+                springPluginManager.startPlugin(pluginInfo.getPluginKey());
+                log.info("[run][插件({}) 启动完成]", pluginInfo.getPluginKey());
+            } catch (Exception e) {
+                log.error("[run][插件({}) 启动异常]", pluginInfo.getPluginKey(), e);
+            }
+        });
+    }
+
+}

+ 21 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStateListener.java

@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.module.iot.framework.plugin.core;
+
+import lombok.extern.slf4j.Slf4j;
+import org.pf4j.PluginStateEvent;
+import org.pf4j.PluginStateListener;
+
+/**
+ * IoT 插件状态监听器,用于 log 插件的状态变化
+ *
+ * @author haohao
+ */
+@Slf4j
+public class IotPluginStateListener implements PluginStateListener {
+
+    @Override
+    public void pluginStateChanged(PluginStateEvent event) {
+        log.info("[pluginStateChanged][插件({}) 状态变化,从 {} 变为 {}]", event.getPlugin().getPluginId(),
+                event.getOldState().toString(), event.getPluginState().toString());
+    }
+
+}

+ 0 - 49
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/PluginStart.java

@@ -1,49 +0,0 @@
-package cn.iocoder.yudao.module.iot.framework.plugin.core;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
-import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInfoDO;
-import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
-import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInfoService;
-import lombok.extern.slf4j.Slf4j;
-import org.pf4j.spring.SpringPluginManager;
-import org.springframework.boot.ApplicationArguments;
-import org.springframework.boot.ApplicationRunner;
-import org.springframework.stereotype.Component;
-
-import javax.annotation.Resource;
-import java.util.List;
-
-// TODO @芋艿:需要 review 下
-@Component
-@Slf4j
-public class PluginStart implements ApplicationRunner {
-
-    @Resource
-    private IotPluginInfoService pluginInfoService;
-
-    @Resource
-    private SpringPluginManager pluginManager;
-
-    @Override
-    public void run(ApplicationArguments args) {
-        TenantUtils.executeIgnore(() -> { // 1. 忽略租户上下文执行
-            List<IotPluginInfoDO> pluginInfoList = pluginInfoService
-                    .getPluginInfoListByStatus(IotPluginStatusEnum.RUNNING.getStatus()); // 2. 获取运行中的插件列表
-            if (CollUtil.isEmpty(pluginInfoList)) { // 3. 检查插件列表是否为空
-                log.info("[run] 没有需要启动的插件"); // 4. 日志记录没有插件需要启动
-                return;
-            }
-            pluginInfoList.forEach(pluginInfo -> { // 5. 使用lambda表达式遍历插件列表
-                try {
-                    log.info("[run][启动插件] pluginKey = {}", pluginInfo.getPluginKey()); // 6. 日志记录插件启动信息
-                    pluginManager.startPlugin(pluginInfo.getPluginKey()); // 7. 启动插件
-                } catch (Exception e) {
-                    log.error("[run][启动插件失败] pluginKey = {}", pluginInfo.getPluginKey(), e); // 8. 记录启动失败的日志
-                }
-            });
-        });
-
-    }
-
-}

+ 1 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoService.java

@@ -14,7 +14,7 @@ import java.util.List;
 /**
  * IoT 插件信息 Service 接口
  *
- * @author 芋道源码
+ * @author haohao
  */
 public interface IotPluginInfoService {
 

+ 17 - 23
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/IotPluginInfoServiceImpl.java

@@ -23,7 +23,7 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PLUGIN_INFO_N
 /**
  * IoT 插件信息 Service 实现类
  *
- * @author 芋道源码
+ * @author haohao
  */
 @Service
 @Validated
@@ -37,7 +37,7 @@ public class IotPluginInfoServiceImpl implements IotPluginInfoService {
     private IotPluginInstanceService pluginInstanceService;
 
     @Resource
-    private SpringPluginManager pluginManager;
+    private SpringPluginManager springPluginManager;
 
     @Override
     public Long createPluginInfo(PluginInfoSaveReqVO createReqVO) {
@@ -60,18 +60,17 @@ public class IotPluginInfoServiceImpl implements IotPluginInfoService {
     public void deletePluginInfo(Long id) {
         // 1.1 校验存在
         IotPluginInfoDO pluginInfoDO = validatePluginInfoExists(id);
-        // 1.2 停止插件
+        // 1.2 未开启状态,才允许删除
         if (IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) {
             throw exception(PLUGIN_INFO_DELETE_FAILED_RUNNING);
         }
 
-        // 2. 卸载插件
+        // 2.1 卸载插件
         pluginInstanceService.stopAndUnloadPlugin(pluginInfoDO.getPluginKey());
-
-        // 3. 删除插件文件
+        // 2.2 删除插件文件
         pluginInstanceService.deletePluginFile(pluginInfoDO);
 
-        // 4. 删除插件信息
+        // 3. 删除插件信息
         pluginInfoMapper.deleteById(id);
     }
 
@@ -97,17 +96,18 @@ public class IotPluginInfoServiceImpl implements IotPluginInfoService {
     public void uploadFile(Long id, MultipartFile file) {
         // 1. 校验插件信息是否存在
         IotPluginInfoDO pluginInfoDo = validatePluginInfoExists(id);
+        // TODO @haohao:最好校验下 file 相关参数,是否完整,类似:version 之类是不是可以解析到
 
-        // 2. 停止并卸载旧的插件
+        // 2.1 停止并卸载旧的插件
         pluginInstanceService.stopAndUnloadPlugin(pluginInfoDo.getPluginKey());
-
-        // 3 上传新的插件文件,更新插件启用状态文件
+        // 2.2 上传新的插件文件,更新插件启用状态文件
         String pluginKeyNew = pluginInstanceService.uploadAndLoadNewPlugin(file);
 
-        // 4. 更新插件信息
+        // 3. 更新插件信息
         updatePluginInfo(pluginInfoDo, pluginKeyNew, file);
     }
 
+    // TODO @haohao:这个方法,要不合并到 uploadFile 里;
     /**
      * 更新插件信息
      *
@@ -116,18 +116,15 @@ public class IotPluginInfoServiceImpl implements IotPluginInfoService {
      * @param file         文件
      */
     private void updatePluginInfo(IotPluginInfoDO pluginInfoDo, String pluginKeyNew, MultipartFile file) {
-        // 创建新的插件信息对象并链式设置属性
         IotPluginInfoDO updatedPluginInfo = new IotPluginInfoDO()
                 .setId(pluginInfoDo.getId())
                 .setPluginKey(pluginKeyNew)
-                .setStatus(IotPluginStatusEnum.STOPPED.getStatus())
+                .setStatus(IotPluginStatusEnum.STOPPED.getStatus()) // TODO @haohao:这个状态,是不是非 stop 哈?
                 .setFileName(file.getOriginalFilename())
-                .setScript("")
-                .setConfigSchema(pluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription())
-                .setVersion(pluginManager.getPlugin(pluginKeyNew).getDescriptor().getVersion())
-                .setDescription(pluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription());
-
-        // 执行更新
+                .setScript("") // TODO @haohao:这个设置为 "" 会不会覆盖数据里的哈?应该从插件里读取?未来?
+                .setConfigSchema(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription())
+                .setVersion(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getVersion())
+                .setDescription(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription());
         pluginInfoMapper.updateById(updatedPluginInfo);
     }
 
@@ -140,10 +137,7 @@ public class IotPluginInfoServiceImpl implements IotPluginInfoService {
         pluginInstanceService.updatePluginStatus(pluginInfoDo, status);
 
         // 3. 更新数据库中的插件状态
-        IotPluginInfoDO updatedPluginInfo = new IotPluginInfoDO();
-        updatedPluginInfo.setId(id);
-        updatedPluginInfo.setStatus(status);
-        pluginInfoMapper.updateById(updatedPluginInfo);
+        pluginInfoMapper.updateById(new IotPluginInfoDO().setId(id).setStatus(status));
     }
 
     @Override

+ 3 - 2
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/IotRuleSceneServiceImpl.java

@@ -404,7 +404,7 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
         // 无 + 禁用、开启:1)存在,删除;TODO 测试:直接删除???(结论:可以)deleteJob
 
         //
-        if (true) {
+        if (false) {
             Long id = 1L;
             Map<String, Object> jobDataMap = IotRuleSceneJob.buildJobDataMap(id);
             schedulerManager.addOrUpdateJob(IotRuleSceneJob.class,
@@ -417,7 +417,8 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
             schedulerManager.pauseJob(IotRuleSceneJob.buildJobName(id));
         }
         if (true) {
-
+            Long id = 1L;
+            schedulerManager.deleteJob(IotRuleSceneJob.buildJobName(id));
         }
     }