Commit b46e630912e6f4c11623b7341ff7a15de0aea0f5

Authored by YunaiV
1 parent d23be861

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

Showing 11 changed files with 119 additions and 110 deletions
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 {
78 78 return success(true);
79 79 }
80 80  
  81 + // TODO @haohao:要不独立一个 VO,不用 PluginInfoSaveReqVO
81 82 @PutMapping("/update-status")
82 83 @Operation(summary = "修改插件状态")
83 84 @PreAuthorize("@ss.hasPermission('iot:plugin-info:update')")
... ...
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 {
13 13  
14 14 // TODO @haohao:一些枚举字段,需要加枚举校验。例如说,deployType、status、type 等
15 15  
16   - @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546")
  16 + @Schema(description = "主键编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546")
17 17 private Long id;
18 18  
19 19 @Schema(description = "插件包标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627")
... ...
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;
9 9 import com.baomidou.mybatisplus.annotation.TableName;
10 10 import lombok.*;
11 11  
  12 +// TODO @haohao:建议 IotPluginInfoDO 改成 IotPluginConfigDO,插件配置。项目里,暂时没有用 info 作为配置的哈。
12 13 /**
13 14 * IoT 插件信息 DO
14 15 *
... ... @@ -38,7 +39,7 @@ public class IotPluginInfoDO extends BaseDO {
38 39 */
39 40 private String name;
40 41 /**
41   - * 描述
  42 + * 插件描述
42 43 */
43 44 private String description;
44 45 /**
... ... @@ -68,12 +69,14 @@ public class IotPluginInfoDO extends BaseDO {
68 69 */
69 70 // TODO @芋艿:枚举字段
70 71 private String protocol;
  72 + // TODO @haohao:这个字段,是不是直接用 CommonStatus,开启、禁用;然后插件实例那,online 是否在线
71 73 /**
72 74 * 状态
73 75 * <p>
74 76 * 枚举 {@link IotPluginStatusEnum}
75 77 */
76 78 private Integer status;
  79 +
77 80 // TODO @芋艿:configSchema、config 示例字段
78 81 /**
79 82 * 插件配置项描述信息
... ... @@ -83,6 +86,7 @@ public class IotPluginInfoDO extends BaseDO {
83 86 * 插件配置信息
84 87 */
85 88 private String config;
  89 +
86 90 // TODO @芋艿:script 后续的使用
87 91 /**
88 92 * 插件脚本
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/config/UnifiedConfiguration.java renamed to yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/config/IotPluginConfiguration.java
1 1 package cn.iocoder.yudao.module.iot.framework.plugin.config;
2 2  
3   -import cn.iocoder.yudao.module.iot.framework.plugin.core.CustomPluginStateListener;
  3 +import cn.iocoder.yudao.module.iot.framework.plugin.core.IotPluginStartRunner;
  4 +import cn.iocoder.yudao.module.iot.framework.plugin.core.IotPluginStateListener;
  5 +import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInfoService;
4 6 import lombok.extern.slf4j.Slf4j;
5 7 import org.pf4j.spring.SpringPluginManager;
6 8 import org.springframework.beans.factory.annotation.Value;
... ... @@ -9,16 +11,24 @@ import org.springframework.context.annotation.Configuration;
9 11  
10 12 import java.nio.file.Paths;
11 13  
12   -// TODO @芋艿:需要 review 下
13   -@Slf4j
  14 +/**
  15 + * IoT 插件配置类
  16 + *
  17 + * @author haohao
  18 + */
14 19 @Configuration
15   -public class UnifiedConfiguration {
  20 +@Slf4j
  21 +public class IotPluginConfiguration {
16 22  
17   - @Value("${pf4j.pluginsDir:pluginsDir}")
18   - private String pluginsDir;
  23 + @Bean
  24 + public IotPluginStartRunner pluginStartRunner(SpringPluginManager pluginManager,
  25 + IotPluginInfoService pluginInfoService) {
  26 + return new IotPluginStartRunner(pluginManager, pluginInfoService);
  27 + }
19 28  
  29 + // TODO @芋艿:需要 review 下
20 30 @Bean
21   - public SpringPluginManager pluginManager() {
  31 + public SpringPluginManager pluginManager(@Value("${pf4j.pluginsDir:pluginsDir}") String pluginsDir) {
22 32 log.info("[init][实例化 SpringPluginManager]");
23 33 SpringPluginManager springPluginManager = new SpringPluginManager(Paths.get(pluginsDir)) {
24 34 // SpringPluginManager springPluginManager = new SpringPluginManager() {
... ... @@ -30,7 +40,7 @@ public class UnifiedConfiguration {
30 40 }
31 41  
32 42 };
33   - springPluginManager.addPluginStateListener(new CustomPluginStateListener());
  43 + springPluginManager.addPluginStateListener(new IotPluginStateListener());
34 44 return springPluginManager;
35 45 }
36 46  
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/CustomPluginStateListener.java deleted
1   -package cn.iocoder.yudao.module.iot.framework.plugin.core;
2   -
3   -import lombok.extern.slf4j.Slf4j;
4   -import org.pf4j.PluginStateEvent;
5   -import org.pf4j.PluginStateListener;
6   -import org.springframework.stereotype.Component;
7   -
8   -// TODO @芋艿:需要 review 下
9   -@Component
10   -@Slf4j
11   -public class CustomPluginStateListener implements PluginStateListener {
12   -
13   - @Override
14   - public void pluginStateChanged(PluginStateEvent event) {
15   - // 1. 获取插件ID
16   - String pluginId = event.getPlugin().getPluginId();
17   - // 2. 获取插件旧状态
18   - String oldState = event.getOldState().toString();
19   - // 3. 获取插件新状态
20   - String newState = event.getPluginState().toString();
21   - // 4. 打印日志信息
22   - log.info("插件的状态 '{}' 已更改为 '{}' 至 '{}'", pluginId, oldState, newState);
23   - }
24   -
25   -}
26 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStartRunner.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.framework.plugin.core;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInfoDO;
  6 +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginDeployTypeEnum;
  7 +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
  8 +import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInfoService;
  9 +import lombok.RequiredArgsConstructor;
  10 +import lombok.extern.slf4j.Slf4j;
  11 +import org.pf4j.spring.SpringPluginManager;
  12 +import org.springframework.boot.ApplicationArguments;
  13 +import org.springframework.boot.ApplicationRunner;
  14 +
  15 +import java.util.List;
  16 +
  17 +/**
  18 + * IoT 插件启动 Runner
  19 + *
  20 + * 用于 Spring Boot 启动时,启动 {@link IotPluginDeployTypeEnum#JAR} 部署类型的插件
  21 + */
  22 +@RequiredArgsConstructor
  23 +@Slf4j
  24 +public class IotPluginStartRunner implements ApplicationRunner {
  25 +
  26 + private final SpringPluginManager springPluginManager;
  27 +
  28 + private final IotPluginInfoService pluginInfoService;
  29 +
  30 + @Override
  31 + public void run(ApplicationArguments args) {
  32 + List<IotPluginInfoDO> pluginInfoList = TenantUtils.executeIgnore(
  33 + // TODO @haohao:需要查询部署类型哈
  34 + () -> pluginInfoService.getPluginInfoListByStatus(IotPluginStatusEnum.RUNNING.getStatus()));
  35 + if (CollUtil.isEmpty(pluginInfoList)) {
  36 + log.info("[run][没有需要启动的插件]");
  37 + return;
  38 + }
  39 +
  40 + // 遍历插件列表,逐个启动
  41 + pluginInfoList.forEach(pluginInfo -> {
  42 + try {
  43 + log.info("[run][插件({}) 启动开始]", pluginInfo.getPluginKey());
  44 + springPluginManager.startPlugin(pluginInfo.getPluginKey());
  45 + log.info("[run][插件({}) 启动完成]", pluginInfo.getPluginKey());
  46 + } catch (Exception e) {
  47 + log.error("[run][插件({}) 启动异常]", pluginInfo.getPluginKey(), e);
  48 + }
  49 + });
  50 + }
  51 +
  52 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/IotPluginStateListener.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.framework.plugin.core;
  2 +
  3 +import lombok.extern.slf4j.Slf4j;
  4 +import org.pf4j.PluginStateEvent;
  5 +import org.pf4j.PluginStateListener;
  6 +
  7 +/**
  8 + * IoT 插件状态监听器,用于 log 插件的状态变化
  9 + *
  10 + * @author haohao
  11 + */
  12 +@Slf4j
  13 +public class IotPluginStateListener implements PluginStateListener {
  14 +
  15 + @Override
  16 + public void pluginStateChanged(PluginStateEvent event) {
  17 + log.info("[pluginStateChanged][插件({}) 状态变化,从 {} 变为 {}]", event.getPlugin().getPluginId(),
  18 + event.getOldState().toString(), event.getPluginState().toString());
  19 + }
  20 +
  21 +}
0 22 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/core/PluginStart.java deleted
1   -package cn.iocoder.yudao.module.iot.framework.plugin.core;
2   -
3   -import cn.hutool.core.collection.CollUtil;
4   -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
5   -import cn.iocoder.yudao.module.iot.dal.dataobject.plugin.IotPluginInfoDO;
6   -import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
7   -import cn.iocoder.yudao.module.iot.service.plugin.IotPluginInfoService;
8   -import lombok.extern.slf4j.Slf4j;
9   -import org.pf4j.spring.SpringPluginManager;
10   -import org.springframework.boot.ApplicationArguments;
11   -import org.springframework.boot.ApplicationRunner;
12   -import org.springframework.stereotype.Component;
13   -
14   -import javax.annotation.Resource;
15   -import java.util.List;
16   -
17   -// TODO @芋艿:需要 review 下
18   -@Component
19   -@Slf4j
20   -public class PluginStart implements ApplicationRunner {
21   -
22   - @Resource
23   - private IotPluginInfoService pluginInfoService;
24   -
25   - @Resource
26   - private SpringPluginManager pluginManager;
27   -
28   - @Override
29   - public void run(ApplicationArguments args) {
30   - TenantUtils.executeIgnore(() -> { // 1. 忽略租户上下文执行
31   - List<IotPluginInfoDO> pluginInfoList = pluginInfoService
32   - .getPluginInfoListByStatus(IotPluginStatusEnum.RUNNING.getStatus()); // 2. 获取运行中的插件列表
33   - if (CollUtil.isEmpty(pluginInfoList)) { // 3. 检查插件列表是否为空
34   - log.info("[run] 没有需要启动的插件"); // 4. 日志记录没有插件需要启动
35   - return;
36   - }
37   - pluginInfoList.forEach(pluginInfo -> { // 5. 使用lambda表达式遍历插件列表
38   - try {
39   - log.info("[run][启动插件] pluginKey = {}", pluginInfo.getPluginKey()); // 6. 日志记录插件启动信息
40   - pluginManager.startPlugin(pluginInfo.getPluginKey()); // 7. 启动插件
41   - } catch (Exception e) {
42   - log.error("[run][启动插件失败] pluginKey = {}", pluginInfo.getPluginKey(), e); // 8. 记录启动失败的日志
43   - }
44   - });
45   - });
46   -
47   - }
48   -
49   -}
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;
14 14 /**
15 15 * IoT 插件信息 Service 接口
16 16 *
17   - * @author 芋道源码
  17 + * @author haohao
18 18 */
19 19 public interface IotPluginInfoService {
20 20  
... ...
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
23 23 /**
24 24 * IoT 插件信息 Service 实现类
25 25 *
26   - * @author 芋道源码
  26 + * @author haohao
27 27 */
28 28 @Service
29 29 @Validated
... ... @@ -37,7 +37,7 @@ public class IotPluginInfoServiceImpl implements IotPluginInfoService {
37 37 private IotPluginInstanceService pluginInstanceService;
38 38  
39 39 @Resource
40   - private SpringPluginManager pluginManager;
  40 + private SpringPluginManager springPluginManager;
41 41  
42 42 @Override
43 43 public Long createPluginInfo(PluginInfoSaveReqVO createReqVO) {
... ... @@ -60,18 +60,17 @@ public class IotPluginInfoServiceImpl implements IotPluginInfoService {
60 60 public void deletePluginInfo(Long id) {
61 61 // 1.1 校验存在
62 62 IotPluginInfoDO pluginInfoDO = validatePluginInfoExists(id);
63   - // 1.2 停止插件
  63 + // 1.2 未开启状态,才允许删除
64 64 if (IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) {
65 65 throw exception(PLUGIN_INFO_DELETE_FAILED_RUNNING);
66 66 }
67 67  
68   - // 2. 卸载插件
  68 + // 2.1 卸载插件
69 69 pluginInstanceService.stopAndUnloadPlugin(pluginInfoDO.getPluginKey());
70   -
71   - // 3. 删除插件文件
  70 + // 2.2 删除插件文件
72 71 pluginInstanceService.deletePluginFile(pluginInfoDO);
73 72  
74   - // 4. 删除插件信息
  73 + // 3. 删除插件信息
75 74 pluginInfoMapper.deleteById(id);
76 75 }
77 76  
... ... @@ -97,17 +96,18 @@ public class IotPluginInfoServiceImpl implements IotPluginInfoService {
97 96 public void uploadFile(Long id, MultipartFile file) {
98 97 // 1. 校验插件信息是否存在
99 98 IotPluginInfoDO pluginInfoDo = validatePluginInfoExists(id);
  99 + // TODO @haohao:最好校验下 file 相关参数,是否完整,类似:version 之类是不是可以解析到
100 100  
101   - // 2. 停止并卸载旧的插件
  101 + // 2.1 停止并卸载旧的插件
102 102 pluginInstanceService.stopAndUnloadPlugin(pluginInfoDo.getPluginKey());
103   -
104   - // 3 上传新的插件文件,更新插件启用状态文件
  103 + // 2.2 上传新的插件文件,更新插件启用状态文件
105 104 String pluginKeyNew = pluginInstanceService.uploadAndLoadNewPlugin(file);
106 105  
107   - // 4. 更新插件信息
  106 + // 3. 更新插件信息
108 107 updatePluginInfo(pluginInfoDo, pluginKeyNew, file);
109 108 }
110 109  
  110 + // TODO @haohao:这个方法,要不合并到 uploadFile 里;
111 111 /**
112 112 * 更新插件信息
113 113 *
... ... @@ -116,18 +116,15 @@ public class IotPluginInfoServiceImpl implements IotPluginInfoService {
116 116 * @param file 文件
117 117 */
118 118 private void updatePluginInfo(IotPluginInfoDO pluginInfoDo, String pluginKeyNew, MultipartFile file) {
119   - // 创建新的插件信息对象并链式设置属性
120 119 IotPluginInfoDO updatedPluginInfo = new IotPluginInfoDO()
121 120 .setId(pluginInfoDo.getId())
122 121 .setPluginKey(pluginKeyNew)
123   - .setStatus(IotPluginStatusEnum.STOPPED.getStatus())
  122 + .setStatus(IotPluginStatusEnum.STOPPED.getStatus()) // TODO @haohao:这个状态,是不是非 stop 哈?
124 123 .setFileName(file.getOriginalFilename())
125   - .setScript("")
126   - .setConfigSchema(pluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription())
127   - .setVersion(pluginManager.getPlugin(pluginKeyNew).getDescriptor().getVersion())
128   - .setDescription(pluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription());
129   -
130   - // 执行更新
  124 + .setScript("") // TODO @haohao:这个设置为 "" 会不会覆盖数据里的哈?应该从插件里读取?未来?
  125 + .setConfigSchema(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription())
  126 + .setVersion(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getVersion())
  127 + .setDescription(springPluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription());
131 128 pluginInfoMapper.updateById(updatedPluginInfo);
132 129 }
133 130  
... ... @@ -140,10 +137,7 @@ public class IotPluginInfoServiceImpl implements IotPluginInfoService {
140 137 pluginInstanceService.updatePluginStatus(pluginInfoDo, status);
141 138  
142 139 // 3. 更新数据库中的插件状态
143   - IotPluginInfoDO updatedPluginInfo = new IotPluginInfoDO();
144   - updatedPluginInfo.setId(id);
145   - updatedPluginInfo.setStatus(status);
146   - pluginInfoMapper.updateById(updatedPluginInfo);
  140 + pluginInfoMapper.updateById(new IotPluginInfoDO().setId(id).setStatus(status));
147 141 }
148 142  
149 143 @Override
... ...
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 {
404 404 // 无 + 禁用、开启:1)存在,删除;TODO 测试:直接删除???(结论:可以)deleteJob
405 405  
406 406 //
407   - if (true) {
  407 + if (false) {
408 408 Long id = 1L;
409 409 Map<String, Object> jobDataMap = IotRuleSceneJob.buildJobDataMap(id);
410 410 schedulerManager.addOrUpdateJob(IotRuleSceneJob.class,
... ... @@ -417,7 +417,8 @@ public class IotRuleSceneServiceImpl implements IotRuleSceneService {
417 417 schedulerManager.pauseJob(IotRuleSceneJob.buildJobName(id));
418 418 }
419 419 if (true) {
420   -
  420 + Long id = 1L;
  421 + schedulerManager.deleteJob(IotRuleSceneJob.buildJobName(id));
421 422 }
422 423 }
423 424  
... ...