Commit 24a660b5c28cac0cd9c6ce3cdc85b456acb54e71

Authored by 安浩浩
1 parent cbfbc55c

【功能完善】IoT: 更新插件管理功能,重构插件标识符为 pluginKey,删除 PluginInstanceController,添加插件实例定时更新任务,优化插件信息获取接口。

Showing 14 changed files with 174 additions and 202 deletions
plugins/enabled.txt
  1 +http-plugin
1 http-plugin@0.0.1 2 http-plugin@0.0.1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/PluginInstanceController.java deleted
1 -package cn.iocoder.yudao.module.iot.controller.admin.plugin;  
2 -  
3 -import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;  
4 -import cn.iocoder.yudao.framework.common.pojo.CommonResult;  
5 -import cn.iocoder.yudao.framework.common.pojo.PageParam;  
6 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
7 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
8 -import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;  
9 -import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInstancePageReqVO;  
10 -import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInstanceRespVO;  
11 -import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInstanceSaveReqVO;  
12 -import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;  
13 -import cn.iocoder.yudao.module.iot.service.plugin.PluginInstanceService;  
14 -import io.swagger.v3.oas.annotations.Operation;  
15 -import io.swagger.v3.oas.annotations.Parameter;  
16 -import io.swagger.v3.oas.annotations.tags.Tag;  
17 -import jakarta.annotation.Resource;  
18 -import jakarta.servlet.http.HttpServletResponse;  
19 -import jakarta.validation.Valid;  
20 -import org.springframework.security.access.prepost.PreAuthorize;  
21 -import org.springframework.validation.annotation.Validated;  
22 -import org.springframework.web.bind.annotation.*;  
23 -  
24 -import java.io.IOException;  
25 -import java.util.List;  
26 -  
27 -import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;  
28 -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;  
29 -  
30 -@Tag(name = "管理后台 - IoT 插件实例")  
31 -@RestController  
32 -@RequestMapping("/iot/plugin-instance")  
33 -@Validated  
34 -public class PluginInstanceController {  
35 -  
36 - @Resource  
37 - private PluginInstanceService pluginInstanceService;  
38 -  
39 - @PostMapping("/create")  
40 - @Operation(summary = "创建IoT 插件实例")  
41 - @PreAuthorize("@ss.hasPermission('iot:plugin-instance:create')")  
42 - public CommonResult<Long> createPluginInstance(@Valid @RequestBody PluginInstanceSaveReqVO createReqVO) {  
43 - return success(pluginInstanceService.createPluginInstance(createReqVO));  
44 - }  
45 -  
46 - @PutMapping("/update")  
47 - @Operation(summary = "更新IoT 插件实例")  
48 - @PreAuthorize("@ss.hasPermission('iot:plugin-instance:update')")  
49 - public CommonResult<Boolean> updatePluginInstance(@Valid @RequestBody PluginInstanceSaveReqVO updateReqVO) {  
50 - pluginInstanceService.updatePluginInstance(updateReqVO);  
51 - return success(true);  
52 - }  
53 -  
54 - @DeleteMapping("/delete")  
55 - @Operation(summary = "删除IoT 插件实例")  
56 - @Parameter(name = "id", description = "编号", required = true)  
57 - @PreAuthorize("@ss.hasPermission('iot:plugin-instance:delete')")  
58 - public CommonResult<Boolean> deletePluginInstance(@RequestParam("id") Long id) {  
59 - pluginInstanceService.deletePluginInstance(id);  
60 - return success(true);  
61 - }  
62 -  
63 - @GetMapping("/get")  
64 - @Operation(summary = "获得IoT 插件实例")  
65 - @Parameter(name = "id", description = "编号", required = true, example = "1024")  
66 - @PreAuthorize("@ss.hasPermission('iot:plugin-instance:query')")  
67 - public CommonResult<PluginInstanceRespVO> getPluginInstance(@RequestParam("id") Long id) {  
68 - PluginInstanceDO pluginInstance = pluginInstanceService.getPluginInstance(id);  
69 - return success(BeanUtils.toBean(pluginInstance, PluginInstanceRespVO.class));  
70 - }  
71 -  
72 - @GetMapping("/page")  
73 - @Operation(summary = "获得IoT 插件实例分页")  
74 - @PreAuthorize("@ss.hasPermission('iot:plugin-instance:query')")  
75 - public CommonResult<PageResult<PluginInstanceRespVO>> getPluginInstancePage(@Valid PluginInstancePageReqVO pageReqVO) {  
76 - PageResult<PluginInstanceDO> pageResult = pluginInstanceService.getPluginInstancePage(pageReqVO);  
77 - return success(BeanUtils.toBean(pageResult, PluginInstanceRespVO.class));  
78 - }  
79 -  
80 -}  
81 \ No newline at end of file 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoRespVO.java
@@ -16,9 +16,9 @@ public class PluginInfoRespVO { @@ -16,9 +16,9 @@ public class PluginInfoRespVO {
16 @ExcelProperty("主键 ID") 16 @ExcelProperty("主键 ID")
17 private Long id; 17 private Long id;
18 18
19 - @Schema(description = "插件包 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627")  
20 - @ExcelProperty("插件包 ID")  
21 - private String pluginId; 19 + @Schema(description = "插件包标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627")
  20 + @ExcelProperty("插件包标识符")
  21 + private String pluginKey;
22 22
23 @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") 23 @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
24 @ExcelProperty("插件名称") 24 @ExcelProperty("插件名称")
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoSaveReqVO.java
@@ -10,8 +10,8 @@ public class PluginInfoSaveReqVO { @@ -10,8 +10,8 @@ public class PluginInfoSaveReqVO {
10 @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546") 10 @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "11546")
11 private Long id; 11 private Long id;
12 12
13 - @Schema(description = "插件包id", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627")  
14 - private String pluginId; 13 + @Schema(description = "插件包标识符", requiredMode = Schema.RequiredMode.REQUIRED, example = "24627")
  14 + private String pluginKey;
15 15
16 @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") 16 @Schema(description = "插件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
17 private String name; 17 private String name;
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininfo/PluginInfoDO.java
@@ -29,12 +29,10 @@ public class PluginInfoDO extends BaseDO { @@ -29,12 +29,10 @@ public class PluginInfoDO extends BaseDO {
29 */ 29 */
30 @TableId 30 @TableId
31 private Long id; 31 private Long id;
32 - // TODO @haohao:这个是不是改成类似 key 之类的字段哈?  
33 - // 回复:默认是 pluginId,可以不用改  
34 /** 32 /**
35 - * 插件包 ID 33 + * 插件包标识符
36 */ 34 */
37 - private String pluginId; 35 + private String pluginKey;
38 /** 36 /**
39 * 插件名称 37 * 插件名称
40 */ 38 */
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/plugininstance/PluginInstanceDO.java
1 package cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance; 1 package cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance;
2 2
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
4 import com.baomidou.mybatisplus.annotation.KeySequence; 5 import com.baomidou.mybatisplus.annotation.KeySequence;
5 import com.baomidou.mybatisplus.annotation.TableId; 6 import com.baomidou.mybatisplus.annotation.TableId;
6 import com.baomidou.mybatisplus.annotation.TableName; 7 import com.baomidou.mybatisplus.annotation.TableName;
7 import lombok.*; 8 import lombok.*;
8 9
9 -// TODO @haohao:一些必要的关联、枚举 10 +
10 /** 11 /**
11 * IoT 插件实例 DO 12 * IoT 插件实例 DO
12 * 13 *
@@ -33,6 +34,8 @@ public class PluginInstanceDO extends BaseDO { @@ -33,6 +34,8 @@ public class PluginInstanceDO extends BaseDO {
33 private String mainId; 34 private String mainId;
34 /** 35 /**
35 * 插件id 36 * 插件id
  37 + * <p>
  38 + * 关联 {@link PluginInfoDO#getId()}
36 */ 39 */
37 private Long pluginId; 40 private Long pluginId;
38 /** 41 /**
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/plugin/PluginInstanceMapper.java
@@ -15,6 +15,12 @@ import org.apache.ibatis.annotations.Mapper; @@ -15,6 +15,12 @@ import org.apache.ibatis.annotations.Mapper;
15 @Mapper 15 @Mapper
16 public interface PluginInstanceMapper extends BaseMapperX<PluginInstanceDO> { 16 public interface PluginInstanceMapper extends BaseMapperX<PluginInstanceDO> {
17 17
  18 + default PluginInstanceDO selectByMainIdAndPluginId(String mainId, Long pluginId) {
  19 + return selectOne(new LambdaQueryWrapperX<PluginInstanceDO>()
  20 + .eq(PluginInstanceDO::getMainId, mainId)
  21 + .eq(PluginInstanceDO::getPluginId, pluginId));
  22 + }
  23 +
18 default PageResult<PluginInstanceDO> selectPage(PluginInstancePageReqVO reqVO) { 24 default PageResult<PluginInstanceDO> selectPage(PluginInstancePageReqVO reqVO) {
19 return selectPage(reqVO, new LambdaQueryWrapperX<PluginInstanceDO>() 25 return selectPage(reqVO, new LambdaQueryWrapperX<PluginInstanceDO>()
20 .eqIfPresent(PluginInstanceDO::getMainId, reqVO.getMainId()) 26 .eqIfPresent(PluginInstanceDO::getMainId, reqVO.getMainId())
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.framework.plugin; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.framework.plugin;
2 2
3 import cn.iocoder.yudao.module.iot.api.ServiceRegistry; 3 import cn.iocoder.yudao.module.iot.api.ServiceRegistry;
4 import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; 4 import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
  5 +import cn.iocoder.yudao.module.iot.framework.plugin.listener.CustomPluginStateListener;
5 import lombok.extern.slf4j.Slf4j; 6 import lombok.extern.slf4j.Slf4j;
6 import org.pf4j.spring.SpringPluginManager; 7 import org.pf4j.spring.SpringPluginManager;
7 import org.springframework.context.annotation.Bean; 8 import org.springframework.context.annotation.Bean;
@@ -30,7 +31,9 @@ public class UnifiedConfiguration { @@ -30,7 +31,9 @@ public class UnifiedConfiguration {
30 @DependsOn(SERVICE_REGISTRY_INITIALIZED_MARKER) 31 @DependsOn(SERVICE_REGISTRY_INITIALIZED_MARKER)
31 public SpringPluginManager pluginManager() { 32 public SpringPluginManager pluginManager() {
32 log.info("[init][实例化 SpringPluginManager]"); 33 log.info("[init][实例化 SpringPluginManager]");
33 - return new SpringPluginManager(); 34 + SpringPluginManager springPluginManager = new SpringPluginManager();
  35 + springPluginManager.addPluginStateListener(new CustomPluginStateListener());
  36 + return springPluginManager;
34 } 37 }
35 38
36 } 39 }
37 \ No newline at end of file 40 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/listener/CustomPluginStateListener.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.framework.plugin.listener;
  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 +@Component
  9 +@Slf4j
  10 +public class CustomPluginStateListener implements PluginStateListener {
  11 +
  12 + @Override
  13 + public void pluginStateChanged(PluginStateEvent event) {
  14 + // 1. 获取插件ID
  15 + String pluginId = event.getPlugin().getPluginId();
  16 + // 2. 获取插件旧状态
  17 + String oldState = event.getOldState().toString();
  18 + // 3. 获取插件新状态
  19 + String newState = event.getPluginState().toString();
  20 + // 4. 打印日志信息
  21 + log.info("插件的状态 '{}' 已更改为 '{}' 至 '{}'", pluginId, oldState, newState);
  22 + }
  23 +
  24 +}
0 \ No newline at end of file 25 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/job/plugin/PluginInstancesJob.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.job.plugin;
  2 +
  3 +
  4 +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
  5 +import cn.iocoder.yudao.module.iot.service.plugin.PluginInstanceService;
  6 +import org.springframework.scheduling.annotation.Scheduled;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import javax.annotation.Resource;
  10 +import java.util.concurrent.TimeUnit;
  11 +
  12 +/**
  13 + * 插件实例 Job
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Component
  18 +public class PluginInstancesJob {
  19 +
  20 + @Resource
  21 + private PluginInstanceService pluginInstanceService;
  22 +
  23 + @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS)
  24 + public void updatePluginInstances() {
  25 + TenantUtils.executeIgnore(() -> {
  26 + pluginInstanceService.updatePluginInstances();
  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/service/plugin/PluginInfoService.java
@@ -71,9 +71,9 @@ public interface PluginInfoService { @@ -71,9 +71,9 @@ public interface PluginInfoService {
71 void updatePluginStatus(Long id, Integer status); 71 void updatePluginStatus(Long id, Integer status);
72 72
73 /** 73 /**
74 - * 获得启用的插件列表 74 + * 获得插件信息列表
75 * 75 *
76 - * @return 插件列表-插件id 76 + * @return 插件信息列表
77 */ 77 */
78 - List<String> getEnabledPlugins(); 78 + List<PluginInfoDO> getPluginInfoList();
79 } 79 }
80 \ No newline at end of file 80 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java
@@ -76,7 +76,7 @@ public class PluginInfoServiceImpl implements PluginInfoService { @@ -76,7 +76,7 @@ public class PluginInfoServiceImpl implements PluginInfoService {
76 } 76 }
77 77
78 // 卸载插件 78 // 卸载插件
79 - PluginWrapper plugin = pluginManager.getPlugin(pluginInfoDO.getPluginId()); 79 + PluginWrapper plugin = pluginManager.getPlugin(pluginInfoDO.getPluginKey());
80 if (plugin != null) { 80 if (plugin != null) {
81 // 查询插件是否是启动状态 81 // 查询插件是否是启动状态
82 if (plugin.getPluginState().equals(PluginState.STARTED)) { 82 if (plugin.getPluginState().equals(PluginState.STARTED)) {
@@ -127,30 +127,30 @@ public class PluginInfoServiceImpl implements PluginInfoService { @@ -127,30 +127,30 @@ public class PluginInfoServiceImpl implements PluginInfoService {
127 // 1. 校验插件信息是否存在 127 // 1. 校验插件信息是否存在
128 PluginInfoDO pluginInfoDo = validatePluginInfoExists(id); 128 PluginInfoDO pluginInfoDo = validatePluginInfoExists(id);
129 129
130 - // 2. 获取插件 ID  
131 - String pluginId = pluginInfoDo.getPluginId(); 130 + // 2. 获取插件标识
  131 + String pluginKey = pluginInfoDo.getPluginKey();
132 132
133 // 3. 停止并卸载旧的插件 133 // 3. 停止并卸载旧的插件
134 - stopAndUnloadPlugin(pluginId); 134 + stopAndUnloadPlugin(pluginKey);
135 135
136 // 4. 上传新的插件文件 136 // 4. 上传新的插件文件
137 - String pluginIdNew = uploadAndLoadNewPlugin(file); 137 + String pluginKeyNew = uploadAndLoadNewPlugin(file);
138 138
139 // 5. 更新插件启用状态文件 139 // 5. 更新插件启用状态文件
140 - updatePluginStatusFile(pluginIdNew, false); 140 + updatePluginStatusFile(pluginKeyNew, false);
141 141
142 // 6. 更新插件信息 142 // 6. 更新插件信息
143 - updatePluginInfo(pluginInfoDo, pluginIdNew, file); 143 + updatePluginInfo(pluginInfoDo, pluginKeyNew, file);
144 } 144 }
145 145
146 // 停止并卸载旧的插件 146 // 停止并卸载旧的插件
147 - private void stopAndUnloadPlugin(String pluginId) {  
148 - PluginWrapper plugin = pluginManager.getPlugin(pluginId); 147 + private void stopAndUnloadPlugin(String pluginKey) {
  148 + PluginWrapper plugin = pluginManager.getPlugin(pluginKey);
149 if (plugin != null) { 149 if (plugin != null) {
150 if (plugin.getPluginState().equals(PluginState.STARTED)) { 150 if (plugin.getPluginState().equals(PluginState.STARTED)) {
151 - pluginManager.stopPlugin(pluginId); // 停止插件 151 + pluginManager.stopPlugin(pluginKey); // 停止插件
152 } 152 }
153 - pluginManager.unloadPlugin(pluginId); // 卸载插件 153 + pluginManager.unloadPlugin(pluginKey); // 卸载插件
154 } 154 }
155 } 155 }
156 156
@@ -175,18 +175,18 @@ public class PluginInfoServiceImpl implements PluginInfoService { @@ -175,18 +175,18 @@ public class PluginInfoServiceImpl implements PluginInfoService {
175 } 175 }
176 176
177 // 更新插件状态文件 177 // 更新插件状态文件
178 - private void updatePluginStatusFile(String pluginIdNew, boolean isEnabled) { 178 + private void updatePluginStatusFile(String pluginKeyNew, boolean isEnabled) {
179 Path enabledFilePath = Paths.get(pluginsDir, "enabled.txt"); 179 Path enabledFilePath = Paths.get(pluginsDir, "enabled.txt");
180 Path disabledFilePath = Paths.get(pluginsDir, "disabled.txt"); 180 Path disabledFilePath = Paths.get(pluginsDir, "disabled.txt");
181 Path targetFilePath = isEnabled ? enabledFilePath : disabledFilePath; 181 Path targetFilePath = isEnabled ? enabledFilePath : disabledFilePath;
182 Path oppositeFilePath = isEnabled ? disabledFilePath : enabledFilePath; 182 Path oppositeFilePath = isEnabled ? disabledFilePath : enabledFilePath;
183 183
184 try { 184 try {
185 - PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginIdNew); 185 + PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginKeyNew);
186 if (pluginWrapper == null) { 186 if (pluginWrapper == null) {
187 throw exception(PLUGIN_INSTALL_FAILED); 187 throw exception(PLUGIN_INSTALL_FAILED);
188 } 188 }
189 - String pluginInfo = pluginIdNew + "@" + pluginWrapper.getDescriptor().getVersion(); 189 + String pluginInfo = pluginKeyNew + "@" + pluginWrapper.getDescriptor().getVersion();
190 List<String> targetLines = Files.exists(targetFilePath) ? Files.readAllLines(targetFilePath) 190 List<String> targetLines = Files.exists(targetFilePath) ? Files.readAllLines(targetFilePath)
191 : new ArrayList<>(); 191 : new ArrayList<>();
192 List<String> oppositeLines = Files.exists(oppositeFilePath) ? Files.readAllLines(oppositeFilePath) 192 List<String> oppositeLines = Files.exists(oppositeFilePath) ? Files.readAllLines(oppositeFilePath)
@@ -209,13 +209,13 @@ public class PluginInfoServiceImpl implements PluginInfoService { @@ -209,13 +209,13 @@ public class PluginInfoServiceImpl implements PluginInfoService {
209 } 209 }
210 210
211 // 更新插件信息 211 // 更新插件信息
212 - private void updatePluginInfo(PluginInfoDO pluginInfoDo, String pluginIdNew, MultipartFile file) {  
213 - pluginInfoDo.setPluginId(pluginIdNew); 212 + private void updatePluginInfo(PluginInfoDO pluginInfoDo, String pluginKeyNew, MultipartFile file) {
  213 + pluginInfoDo.setPluginKey(pluginKeyNew);
214 pluginInfoDo.setStatus(IotPluginStatusEnum.STOPPED.getStatus()); 214 pluginInfoDo.setStatus(IotPluginStatusEnum.STOPPED.getStatus());
215 pluginInfoDo.setFileName(file.getOriginalFilename()); 215 pluginInfoDo.setFileName(file.getOriginalFilename());
216 pluginInfoDo.setScript(""); 216 pluginInfoDo.setScript("");
217 217
218 - PluginDescriptor pluginDescriptor = pluginManager.getPlugin(pluginIdNew).getDescriptor(); 218 + PluginDescriptor pluginDescriptor = pluginManager.getPlugin(pluginKeyNew).getDescriptor();
219 pluginInfoDo.setConfigSchema(pluginDescriptor.getPluginDescription()); 219 pluginInfoDo.setConfigSchema(pluginDescriptor.getPluginDescription());
220 pluginInfoDo.setVersion(pluginDescriptor.getVersion()); 220 pluginInfoDo.setVersion(pluginDescriptor.getVersion());
221 pluginInfoDo.setDescription(pluginDescriptor.getPluginDescription()); 221 pluginInfoDo.setDescription(pluginDescriptor.getPluginDescription());
@@ -232,23 +232,23 @@ public class PluginInfoServiceImpl implements PluginInfoService { @@ -232,23 +232,23 @@ public class PluginInfoServiceImpl implements PluginInfoService {
232 throw exception(PLUGIN_STATUS_INVALID); 232 throw exception(PLUGIN_STATUS_INVALID);
233 } 233 }
234 234
235 - // 3. 获取插件ID和插件实例  
236 - String pluginId = pluginInfoDo.getPluginId();  
237 - PluginWrapper plugin = pluginManager.getPlugin(pluginId); 235 + // 3. 获取插件标识和插件实例
  236 + String pluginKey = pluginInfoDo.getPluginKey();
  237 + PluginWrapper plugin = pluginManager.getPlugin(pluginKey);
238 238
239 // 4. 根据状态更新插件 239 // 4. 根据状态更新插件
240 if (plugin != null) { 240 if (plugin != null) {
241 // 4.1 如果目标状态是运行且插件未启动,则启动插件 241 // 4.1 如果目标状态是运行且插件未启动,则启动插件
242 if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) 242 if (status.equals(IotPluginStatusEnum.RUNNING.getStatus())
243 && plugin.getPluginState() != PluginState.STARTED) { 243 && plugin.getPluginState() != PluginState.STARTED) {
244 - pluginManager.startPlugin(pluginId);  
245 - updatePluginStatusFile(pluginId, true); // 更新插件状态文件为启用 244 + pluginManager.startPlugin(pluginKey);
  245 + updatePluginStatusFile(pluginKey, true); // 更新插件状态文件为启用
246 } 246 }
247 // 4.2 如果目标状态是停止且插件已启动,则停止插件 247 // 4.2 如果目标状态是停止且插件已启动,则停止插件
248 else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) 248 else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus())
249 && plugin.getPluginState() == PluginState.STARTED) { 249 && plugin.getPluginState() == PluginState.STARTED) {
250 - pluginManager.stopPlugin(pluginId);  
251 - updatePluginStatusFile(pluginId, false); // 更新插件状态文件为禁用 250 + pluginManager.stopPlugin(pluginKey);
  251 + updatePluginStatusFile(pluginKey, false); // 更新插件状态文件为禁用
252 } 252 }
253 } else { 253 } else {
254 // 5. 插件不存在且状态为停止,抛出异常 254 // 5. 插件不存在且状态为停止,抛出异常
@@ -263,11 +263,8 @@ public class PluginInfoServiceImpl implements PluginInfoService { @@ -263,11 +263,8 @@ public class PluginInfoServiceImpl implements PluginInfoService {
263 } 263 }
264 264
265 @Override 265 @Override
266 - public List<String> getEnabledPlugins() {  
267 - return pluginInfoMapper.selectList().stream()  
268 - .filter(pluginInfoDO -> IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus()))  
269 - .map(PluginInfoDO::getPluginId)  
270 - .toList(); 266 + public List<PluginInfoDO> getPluginInfoList() {
  267 + return pluginInfoMapper.selectList(null);
271 } 268 }
272 269
273 } 270 }
274 \ No newline at end of file 271 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java
1 package cn.iocoder.yudao.module.iot.service.plugin; 1 package cn.iocoder.yudao.module.iot.service.plugin;
2 2
3 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
4 -import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInstancePageReqVO;  
5 -import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInstanceSaveReqVO;  
6 -import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;  
7 -import jakarta.validation.Valid;  
8 -  
9 /** 3 /**
10 * IoT 插件实例 Service 接口 4 * IoT 插件实例 Service 接口
11 * 5 *
@@ -14,41 +8,8 @@ import jakarta.validation.Valid; @@ -14,41 +8,8 @@ import jakarta.validation.Valid;
14 public interface PluginInstanceService { 8 public interface PluginInstanceService {
15 9
16 /** 10 /**
17 - * 创建IoT 插件实例  
18 - *  
19 - * @param createReqVO 创建信息  
20 - * @return 编号  
21 - */  
22 - Long createPluginInstance(@Valid PluginInstanceSaveReqVO createReqVO);  
23 -  
24 - /**  
25 * 更新IoT 插件实例 11 * 更新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 */ 12 */
52 - PageResult<PluginInstanceDO> getPluginInstancePage(PluginInstancePageReqVO pageReqVO); 13 + void updatePluginInstances();
53 14
54 } 15 }
55 \ 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/plugin/PluginInstanceServiceImpl.java
1 package cn.iocoder.yudao.module.iot.service.plugin; 1 package cn.iocoder.yudao.module.iot.service.plugin;
2 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.plugin.vo.PluginInstancePageReqVO;  
6 -import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInstanceSaveReqVO; 3 +import cn.hutool.core.net.NetUtil;
  4 +import cn.hutool.core.util.IdUtil;
  5 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
7 import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO; 6 import cn.iocoder.yudao.module.iot.dal.dataobject.plugininstance.PluginInstanceDO;
8 import cn.iocoder.yudao.module.iot.dal.mysql.plugin.PluginInstanceMapper; 7 import cn.iocoder.yudao.module.iot.dal.mysql.plugin.PluginInstanceMapper;
9 import jakarta.annotation.Resource; 8 import jakarta.annotation.Resource;
  9 +import lombok.extern.slf4j.Slf4j;
  10 +import org.pf4j.PluginWrapper;
  11 +import org.pf4j.spring.SpringPluginManager;
  12 +import org.springframework.beans.factory.annotation.Value;
10 import org.springframework.stereotype.Service; 13 import org.springframework.stereotype.Service;
11 import org.springframework.validation.annotation.Validated; 14 import org.springframework.validation.annotation.Validated;
12 15
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; 16 +import java.util.List;
15 17
16 /** 18 /**
17 * IoT 插件实例 Service 实现类 19 * IoT 插件实例 Service 实现类
@@ -20,51 +22,79 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PLUGIN_INSTAN @@ -20,51 +22,79 @@ import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PLUGIN_INSTAN
20 */ 22 */
21 @Service 23 @Service
22 @Validated 24 @Validated
  25 +@Slf4j
23 public class PluginInstanceServiceImpl implements PluginInstanceService { 26 public class PluginInstanceServiceImpl implements PluginInstanceService {
24 27
  28 + /**
  29 + * 主程序id
  30 + */
  31 + public static final String MAIN_ID = IdUtil.fastSimpleUUID();
  32 +
  33 + @Resource
  34 + private PluginInfoService pluginInfoService;
25 @Resource 35 @Resource
26 private PluginInstanceMapper pluginInstanceMapper; 36 private PluginInstanceMapper pluginInstanceMapper;
  37 + @Resource
  38 + private SpringPluginManager pluginManager;
27 39
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 - } 40 + @Value("${server.port:48080}")
  41 + private int port;
36 42
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 43
46 @Override 44 @Override
47 - public void deletePluginInstance(Long id) {  
48 - // 校验存在  
49 - validatePluginInstanceExists(id);  
50 - // 删除  
51 - pluginInstanceMapper.deleteById(id);  
52 - } 45 + public void updatePluginInstances() {
  46 + // 1. 查询 pf4j 插件列表
  47 + List<PluginWrapper> plugins = pluginManager.getPlugins();
53 48
54 - private void validatePluginInstanceExists(Long id) {  
55 - if (pluginInstanceMapper.selectById(id) == null) {  
56 - throw exception(PLUGIN_INSTANCE_NOT_EXISTS);  
57 - }  
58 - } 49 + // 2. 查询插件信息列表
  50 + List<PluginInfoDO> pluginInfos = pluginInfoService.getPluginInfoList();
59 51
60 - @Override  
61 - public PluginInstanceDO getPluginInstance(Long id) {  
62 - return pluginInstanceMapper.selectById(id); 52 + // 动态获取主程序的 IP 和端口
  53 + String mainIp = getLocalIpAddress();
  54 +
  55 + // 3. 遍历插件列表,并保存为插件实例
  56 + for (PluginWrapper plugin : plugins) {
  57 + String pluginKey = plugin.getPluginId();
  58 + PluginInfoDO pluginInfo = pluginInfos.stream()
  59 + .filter(pluginInfoDO -> pluginInfoDO.getPluginKey().equals(pluginKey))
  60 + .findFirst()
  61 + .orElse(null);
  62 +
  63 + // 4. 如果插件信息不存在,则跳过
  64 + if (pluginInfo == null) {
  65 + continue;
  66 + }
  67 +
  68 + // 5. 查询插件实例
  69 + PluginInstanceDO pluginInstance = pluginInstanceMapper.selectByMainIdAndPluginId(MAIN_ID, pluginInfo.getId());
  70 +
  71 + // 6. 如果插件实例不存在,则创建
  72 + if (pluginInstance == null) {
  73 + pluginInstance = new PluginInstanceDO();
  74 + pluginInstance.setPluginId(pluginInfo.getId());
  75 + pluginInstance.setMainId(MAIN_ID);
  76 + pluginInstance.setIp(mainIp);
  77 + pluginInstance.setPort(port);
  78 + pluginInstance.setHeartbeatAt(System.currentTimeMillis());
  79 + pluginInstanceMapper.insert(pluginInstance);
  80 + } else {
  81 + // 7. 如果插件实例存在,则更新
  82 + pluginInstance.setHeartbeatAt(System.currentTimeMillis());
  83 + pluginInstanceMapper.updateById(pluginInstance);
  84 + }
  85 + }
63 } 86 }
64 87
65 - @Override  
66 - public PageResult<PluginInstanceDO> getPluginInstancePage(PluginInstancePageReqVO pageReqVO) {  
67 - return pluginInstanceMapper.selectPage(pageReqVO); 88 + private String getLocalIpAddress() {
  89 + try {
  90 + List<String> ipList = NetUtil.localIpv4s().stream()
  91 + .filter(ip -> !ip.startsWith("0.0") && !ip.startsWith("127.") && !ip.startsWith("169.254") && !ip.startsWith("255.255.255.255"))
  92 + .toList();
  93 + return ipList.isEmpty() ? "127.0.0.1" : ipList.get(0);
  94 + } catch (Exception e) {
  95 + log.error("获取本地IP地址失败", e);
  96 + return "127.0.0.1"; // 默认值
  97 + }
68 } 98 }
69 99
70 } 100 }
71 \ No newline at end of file 101 \ No newline at end of file