diff --git a/backend/src/main/java/io/dataease/base/domain/MyPlugin.java b/backend/src/main/java/io/dataease/base/domain/MyPlugin.java index e7c9e0ca76..a79291d8e1 100644 --- a/backend/src/main/java/io/dataease/base/domain/MyPlugin.java +++ b/backend/src/main/java/io/dataease/base/domain/MyPlugin.java @@ -35,5 +35,7 @@ public class MyPlugin implements Serializable { private String icon; + private String require = "1.9.0"; + private static final long serialVersionUID = 1L; } \ No newline at end of file diff --git a/backend/src/main/java/io/dataease/commons/utils/CodingUtil.java b/backend/src/main/java/io/dataease/commons/utils/CodingUtil.java index ec283b549b..714392e36f 100644 --- a/backend/src/main/java/io/dataease/commons/utils/CodingUtil.java +++ b/backend/src/main/java/io/dataease/commons/utils/CodingUtil.java @@ -195,4 +195,16 @@ public class CodingUtil { } return shortBuffer.toString(); } + + public static Integer string2Integer(String str) { + StringBuffer sb = new StringBuffer(); + if (StringUtils.isBlank(str)) return null; + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (Character.isDigit(c)) { + sb.append(c); + } + } + return sb.length() > 0 ? Integer.parseInt(sb.toString()) : null; + } } diff --git a/backend/src/main/java/io/dataease/service/sys/PluginService.java b/backend/src/main/java/io/dataease/service/sys/PluginService.java index 282f3fe68f..cce8a8b175 100644 --- a/backend/src/main/java/io/dataease/service/sys/PluginService.java +++ b/backend/src/main/java/io/dataease/service/sys/PluginService.java @@ -1,17 +1,22 @@ package io.dataease.service.sys; +import cn.hutool.core.io.FileUtil; import com.google.gson.Gson; import io.dataease.base.domain.MyPlugin; import io.dataease.base.mapper.MyPluginMapper; import io.dataease.base.mapper.ext.ExtSysPluginMapper; import io.dataease.base.mapper.ext.query.GridExample; import io.dataease.commons.constants.AuthConstants; +import io.dataease.commons.exception.DEException; +import io.dataease.commons.utils.CodingUtil; import io.dataease.commons.utils.DeFileUtils; +import io.dataease.commons.utils.LogUtil; import io.dataease.commons.utils.ZipUtils; import io.dataease.controller.sys.base.BaseGridRequest; import io.dataease.listener.util.CacheUtils; import io.dataease.plugins.config.LoadjarUtil; import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -22,8 +27,10 @@ import javax.annotation.Resource; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; @Service @@ -43,6 +50,9 @@ public class PluginService { @Autowired private LoadjarUtil loadjarUtil; + @Value("${version}") + private String version; + public List query(BaseGridRequest request) { GridExample gridExample = request.convertExample(); @@ -66,7 +76,9 @@ public class PluginService { DeFileUtils.deleteFile(pluginDir + "temp/"); DeFileUtils.deleteFile(folder); // 需要删除文件 - e.printStackTrace(); + // e.printStackTrace(); + LogUtil.error(e.getMessage(), e); + DEException.throwException(e); } //3.解析plugin.json 失败则 直接返回错误 删除文件 File folderFile = new File(folder); @@ -74,15 +86,31 @@ public class PluginService { if (ArrayUtils.isEmpty(jsonFiles)) { DeFileUtils.deleteFile(pluginDir + "temp/"); DeFileUtils.deleteFile(folder); - throw new RuntimeException("缺少插件描述文件"); + String msg = "缺少插件描述文件【plugin.json】"; + LogUtil.error(msg); + DEException.throwException(new RuntimeException(msg)); } MyPlugin myPlugin = formatJsonFile(jsonFiles[0]); + + if (!versionMatch(myPlugin.getRequire())) { + String msg = "当前插件要求系统版本最低为:" + myPlugin.getRequire(); + LogUtil.error(msg); + DEException.throwException(new RuntimeException(msg)); + } //4.加载jar包 失败则 直接返回错误 删除文件 File[] jarFiles = folderFile.listFiles(this::isPluginJar); if (ArrayUtils.isEmpty(jarFiles)) { DeFileUtils.deleteFile(pluginDir + "temp/"); DeFileUtils.deleteFile(folder); - throw new RuntimeException("缺少插件jar文件"); + String msg = "缺少插件jar文件"; + LogUtil.error(msg); + DEException.throwException(new RuntimeException(msg)); + } + + if (pluginExist(myPlugin)) { + String msg = "插件【"+myPlugin.getName()+"】已存在,请先卸载"; + LogUtil.error(msg); + DEException.throwException(new RuntimeException(msg)); } String targetDir = null; try { @@ -100,7 +128,8 @@ public class PluginService { if (StringUtils.isNotEmpty(targetDir)) { DeFileUtils.deleteFile(targetDir); } - e.printStackTrace(); + LogUtil.error(e.getMessage(), e); + DEException.throwException(e); } finally { DeFileUtils.deleteFile(pluginDir + "temp/"); DeFileUtils.deleteFile(folder); @@ -122,6 +151,19 @@ public class PluginService { return dir; } + /** + * 检测插件是否已存在 + * @param myPlugin + * @return + */ + public boolean pluginExist(MyPlugin myPlugin) { + GridExample gridExample = new GridExample(); + List plugins = extSysPluginMapper.query(gridExample); + return plugins.stream().anyMatch(plugin -> { + return StringUtils.equals(myPlugin.getName(), plugin.getName()) || StringUtils.equals(myPlugin.getModuleName(), plugin.getModuleName()); + }); + } + /** * 卸载插件 * @@ -129,6 +171,13 @@ public class PluginService { * @return */ public Boolean uninstall(Long pluginId) { + MyPlugin myPlugin = myPluginMapper.selectByPrimaryKey(pluginId); + if (ObjectUtils.isEmpty(myPlugin)) { + String msg = "当前插件不存在"; + LogUtil.error(msg); + DEException.throwException(new RuntimeException(msg)); + } + deleteJarFile(myPlugin); CacheUtils.removeAll(AuthConstants.USER_CACHE_NAME); CacheUtils.removeAll(AuthConstants.USER_ROLE_CACHE_NAME); CacheUtils.removeAll(AuthConstants.USER_PERMISSION_CACHE_NAME); @@ -136,6 +185,15 @@ public class PluginService { return true; } + private void deleteJarFile(MyPlugin plugin) { + String version = plugin.getVersion(); + String moduleName = plugin.getModuleName(); + String fileName = moduleName + "-" + version + ".jar"; + String path = pluginDir + plugin.getStore() + "/" + fileName; + File jarFile = new File(path); + FileUtil.del(jarFile); + } + /** * 改变插件状态 * @@ -195,4 +253,16 @@ public class PluginService { public Map remoteInstall(Map params) { return null; } + + public boolean versionMatch(String pluginVersion) { + List versionLists = Arrays.stream(version.split(".")).map(CodingUtil::string2Integer).collect(Collectors.toList()); + List requireVersionLists = Arrays.stream(pluginVersion.split(".")).map(CodingUtil::string2Integer).collect(Collectors.toList()); + int maxSize = Math.max(versionLists.size(), requireVersionLists.size()); + for (int i = 0; i < maxSize; i++) { + Integer currentV = versionLists.size() == i ? 0 : versionLists.get(i); + Integer requireV = requireVersionLists.size() == i ? 0 : requireVersionLists.get(i); + if (requireV > currentV) return false; + } + return false; + } } diff --git a/frontend/src/lang/en.js b/frontend/src/lang/en.js index 9758b4c53b..c3ff4f0aa0 100644 --- a/frontend/src/lang/en.js +++ b/frontend/src/lang/en.js @@ -1645,7 +1645,9 @@ export default { uninstall_cancel: 'Cancel uninstall plugin', setting_background: 'BackGround', setting_jump: 'Jump Setting', - select_view: 'Select View' + select_view: 'Select View', + un_install_success: 'Uninstall is successful and restart takes effect', + un_install_error: 'Uninstall failed, please contact the administrator' }, display: { logo: 'Head system logo', diff --git a/frontend/src/lang/tw.js b/frontend/src/lang/tw.js index 8a8bfa1c4b..ff9d4aa51b 100644 --- a/frontend/src/lang/tw.js +++ b/frontend/src/lang/tw.js @@ -1644,9 +1644,11 @@ export default { creator: '作者', install_time: '安裝時間', release_time: '時間', - un_install: '卸載', + un_install: '卸載(重啟服務後生效)', uninstall_confirm: '確定卸載該插件', - uninstall_cancel: '取消卸載插件' + uninstall_cancel: '取消卸載插件', + un_install_success: '卸載成功,重啟生效', + un_install_error: '卸載失敗,請聯系管理員' }, display: { logo: '頭部繫統logo', diff --git a/frontend/src/lang/zh.js b/frontend/src/lang/zh.js index f29106533f..79ea248c7d 100644 --- a/frontend/src/lang/zh.js +++ b/frontend/src/lang/zh.js @@ -1653,9 +1653,11 @@ export default { creator: '作者', install_time: '安装时间', release_time: '时间', - un_install: '卸载', + un_install: '卸载(卸载并重启服务后生效)', uninstall_confirm: '确定卸载该插件', - uninstall_cancel: '取消卸载插件' + uninstall_cancel: '取消卸载插件', + un_install_success: '卸载成功,重启生效', + un_install_error: '卸载失败,请联系管理员' }, display: { logo: '头部系统logo', diff --git a/frontend/src/views/system/plugin/index.vue b/frontend/src/views/system/plugin/index.vue index 99c08b8f62..3713f245ee 100644 --- a/frontend/src/views/system/plugin/index.vue +++ b/frontend/src/views/system/plugin/index.vue @@ -8,7 +8,6 @@ @search="search" > - + @@ -72,6 +71,10 @@ export default { // label: this.$t('commons.delete'), icon: 'el-icon-delete', type: 'danger', click: this.del, // show: checkPermission(['user:del']) // } + { + label: this.$t('plugin.un_install'), icon: 'el-icon-delete', type: 'danger', click: this.del, + disabled: this.btnDisabled + } ], searchConfig: { useQuickSearch: true, @@ -113,6 +116,8 @@ export default { this.uploading = true }, uploadFail(response, file, fileList) { + const msg = response && response.message || '安装失败' + this.$error(msg) this.uploading = false }, uploadSuccess(response, file, fileList) { @@ -121,20 +126,23 @@ export default { }, del(row) { - this.$confirm(this.$t('user.delete_confirm'), '', { + this.$confirm(this.$t('plugin.uninstall_confirm'), '', { confirmButtonText: this.$t('commons.confirm'), cancelButtonText: this.$t('commons.cancel'), type: 'warning' }).then(() => { uninstall(row.pluginId).then(res => { this.search() - this.$success('卸载成功') + this.$success(this.$t('plugin.un_install_success')) }).catch(() => { - this.$error('卸载失败') + this.$error(this.$t('plugin.un_install_error')) }) }).catch(() => { - this.$info(this.$t('commons.delete_cancel')) + this.$info(this.$t('plugin.uninstall_cancel')) }) + }, + btnDisabled(row) { + return row.pluginId < 4 } }