【代码评审】IoT:plugin 相关的实现
This commit is contained in:
parent
d23be86164
commit
b46e630912
|
@ -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')")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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 后续的使用
|
||||
/**
|
||||
* 插件脚本
|
||||
|
|
|
@ -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 {
|
||||
|
||||
@Value("${pf4j.pluginsDir:pluginsDir}")
|
||||
private String pluginsDir;
|
||||
@Slf4j
|
||||
public class IotPluginConfiguration {
|
||||
|
||||
@Bean
|
||||
public SpringPluginManager pluginManager() {
|
||||
public IotPluginStartRunner pluginStartRunner(SpringPluginManager pluginManager,
|
||||
IotPluginInfoService pluginInfoService) {
|
||||
return new IotPluginStartRunner(pluginManager, pluginInfoService);
|
||||
}
|
||||
|
||||
// TODO @芋艿:需要 review 下
|
||||
@Bean
|
||||
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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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. 记录启动失败的日志
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,7 @@ import java.util.List;
|
|||
/**
|
||||
* IoT 插件信息 Service 接口
|
||||
*
|
||||
* @author 芋道源码
|
||||
* @author haohao
|
||||
*/
|
||||
public interface IotPluginInfoService {
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue