Explorar el Código

!38 V_1.1.0
Merge pull request !38 from AE86/V_1.0.0

AE86 hace 3 años
padre
commit
34db281d26
Se han modificado 39 ficheros con 402 adiciones y 93 borrados
  1. 1 1
      README.md
  2. 1 2
      dbsyncer-biz/pom.xml
  3. 9 2
      dbsyncer-biz/src/main/java/org/dbsyncer/biz/PluginService.java
  4. 6 6
      dbsyncer-biz/src/main/java/org/dbsyncer/biz/checker/AbstractChecker.java
  5. 6 2
      dbsyncer-biz/src/main/java/org/dbsyncer/biz/impl/MappingServiceImpl.java
  6. 37 2
      dbsyncer-biz/src/main/java/org/dbsyncer/biz/impl/PluginServiceImpl.java
  7. 19 0
      dbsyncer-biz/src/main/java/org/dbsyncer/biz/vo/PluginVo.java
  8. 1 2
      dbsyncer-cache/pom.xml
  9. 1 2
      dbsyncer-cluster/pom.xml
  10. 1 1
      dbsyncer-common/pom.xml
  11. 1 1
      dbsyncer-common/src/main/java/org/dbsyncer/common/spi/ConvertService.java
  12. 1 2
      dbsyncer-connector/pom.xml
  13. 1 2
      dbsyncer-listener/pom.xml
  14. 6 1
      dbsyncer-listener/src/main/java/org/dbsyncer/listener/oracle/dcn/DBChangeNotification.java
  15. 1 2
      dbsyncer-manager/pom.xml
  16. 2 0
      dbsyncer-manager/src/main/java/org/dbsyncer/manager/Manager.java
  17. 5 0
      dbsyncer-manager/src/main/java/org/dbsyncer/manager/ManagerFactory.java
  18. 1 2
      dbsyncer-monitor/pom.xml
  19. 1 2
      dbsyncer-parser/pom.xml
  20. 1 2
      dbsyncer-plugin/pom.xml
  21. 39 8
      dbsyncer-plugin/src/main/java/org/dbsyncer/plugin/PluginFactory.java
  22. 16 0
      dbsyncer-plugin/src/main/java/org/dbsyncer/plugin/config/Plugin.java
  23. 40 0
      dbsyncer-plugin/src/main/java/org/dbsyncer/plugin/service/DemoConvertServiceImpl.java
  24. 1 2
      dbsyncer-storage/pom.xml
  25. 1 2
      dbsyncer-web/pom.xml
  26. 41 2
      dbsyncer-web/src/main/java/org/dbsyncer/web/controller/upload/UploadController.java
  27. 2 2
      dbsyncer-web/src/main/resources/application.properties
  28. 3 3
      dbsyncer-web/src/main/resources/public/index/index.html
  29. 8 0
      dbsyncer-web/src/main/resources/public/mapping/add.html
  30. 2 2
      dbsyncer-web/src/main/resources/public/mapping/edit.html
  31. 2 2
      dbsyncer-web/src/main/resources/public/mapping/editTableGroup.html
  32. 128 33
      dbsyncer-web/src/main/resources/public/upload/upload.html
  33. 2 0
      dbsyncer-web/src/main/resources/static/css/common.css
  34. BIN
      dbsyncer-web/src/main/resources/static/img/plugin/jar.png
  35. BIN
      dbsyncer-web/src/main/resources/static/img/plugin/spi.png
  36. 12 0
      dbsyncer-web/src/main/resources/static/js/mapping/add.js
  37. 1 1
      dbsyncer-web/src/main/resources/static/js/monitor/index.js
  38. 1 1
      pom.xml
  39. 1 1
      version.cmd

+ 1 - 1
README.md

@@ -199,7 +199,7 @@
     </p>
     <p>上传插件</p>
     <p align="center">
-        <img src="https://images.gitee.com/uploads/images/2021/0727/235455_1ebda1f0_376718.png" />
+        <img src="https://images.gitee.com/uploads/images/2021/0806/232643_9b1f3f64_376718.png" />
     </p>
     <h3>流程图</h3>
     <p align="center">

+ 1 - 2
dbsyncer-biz/pom.xml

@@ -5,10 +5,9 @@
 	<parent>
 		<artifactId>dbsyncer</artifactId>
 		<groupId>org.ghi</groupId>
-		<version>1.0.9-Alpha</version>
+		<version>1.1.0-Alpha</version>
 	</parent>
 	<modelVersion>4.0.0</modelVersion>
-
 	<artifactId>dbsyncer-biz</artifactId>
 
 	<dependencies>

+ 9 - 2
dbsyncer-biz/src/main/java/org/dbsyncer/biz/PluginService.java

@@ -1,6 +1,6 @@
 package org.dbsyncer.biz;
 
-import org.dbsyncer.plugin.config.Plugin;
+import org.dbsyncer.biz.vo.PluginVo;
 
 import java.util.List;
 
@@ -16,7 +16,7 @@ public interface PluginService {
      *
      * @return
      */
-    List<Plugin> getPluginAll();
+    List<PluginVo> getPluginAll();
 
     /**
      * 获取插件上传路径
@@ -25,6 +25,13 @@ public interface PluginService {
      */
     String getPluginPath();
 
+    /**
+     * 获取开发包路径
+     *
+     * @return
+     */
+    String getLibraryPath();
+
     /**
      * 加载插件
      */

+ 6 - 6
dbsyncer-biz/src/main/java/org/dbsyncer/biz/checker/AbstractChecker.java

@@ -2,13 +2,13 @@ package org.dbsyncer.biz.checker;
 
 import org.apache.commons.lang.StringUtils;
 import org.dbsyncer.biz.BizException;
-import org.dbsyncer.biz.PluginService;
 import org.dbsyncer.common.util.CollectionUtils;
 import org.dbsyncer.common.util.JsonUtil;
 import org.dbsyncer.connector.config.Filter;
-import org.dbsyncer.parser.model.Convert;
+import org.dbsyncer.manager.Manager;
 import org.dbsyncer.parser.model.AbstractConfigModel;
 import org.dbsyncer.parser.model.ConfigModel;
+import org.dbsyncer.parser.model.Convert;
 import org.dbsyncer.plugin.config.Plugin;
 import org.dbsyncer.storage.SnowflakeIdWorker;
 import org.dbsyncer.storage.constant.ConfigConstant;
@@ -29,7 +29,7 @@ import java.util.*;
 public abstract class AbstractChecker implements Checker {
 
     @Autowired
-    private PluginService pluginService;
+    private Manager manager;
 
     @Autowired
     private SnowflakeIdWorker snowflakeIdWorker;
@@ -85,7 +85,7 @@ public abstract class AbstractChecker implements Checker {
         String pluginClassName = params.get("pluginClassName");
         Plugin plugin = null;
         if (StringUtils.isNotBlank(pluginClassName)) {
-            List<Plugin> plugins = pluginService.getPluginAll();
+            List<Plugin> plugins = manager.getPluginAll();
             if (!CollectionUtils.isEmpty(plugins)) {
                 for (Plugin p : plugins) {
                     if (StringUtils.equals(p.getClassName(), pluginClassName)) {
@@ -98,10 +98,10 @@ public abstract class AbstractChecker implements Checker {
         model.setPlugin(plugin);
     }
 
-    private <T> List<T> jsonToList(String json, Class<T> valueType){
+    private <T> List<T> jsonToList(String json, Class<T> valueType) {
         try {
             JSONArray array = new JSONArray(json);
-            if(null != array){
+            if (null != array) {
                 List<T> list = new ArrayList<>();
                 int length = array.length();
                 for (int i = 0; i < length; i++) {

+ 6 - 2
dbsyncer-biz/src/main/java/org/dbsyncer/biz/impl/MappingServiceImpl.java

@@ -1,5 +1,6 @@
 package org.dbsyncer.biz.impl;
 
+import org.apache.commons.lang.StringUtils;
 import org.dbsyncer.biz.BizException;
 import org.dbsyncer.biz.MappingService;
 import org.dbsyncer.biz.TableGroupService;
@@ -49,8 +50,11 @@ public class MappingServiceImpl extends BaseServiceImpl implements MappingServic
 
         String id = manager.addMapping(model);
 
-        // 匹配相似表
-        matchSimilarTable(model);
+        // 匹配相似表 on
+        String autoMatchTable = params.get("autoMatchTable");
+        if(StringUtils.isNotBlank(autoMatchTable)){
+            matchSimilarTable(model);
+        }
 
         return id;
     }

+ 37 - 2
dbsyncer-biz/src/main/java/org/dbsyncer/biz/impl/PluginServiceImpl.java

@@ -3,15 +3,23 @@ package org.dbsyncer.biz.impl;
 import org.apache.commons.lang.StringUtils;
 import org.dbsyncer.biz.BizException;
 import org.dbsyncer.biz.PluginService;
+import org.dbsyncer.biz.vo.PluginVo;
+import org.dbsyncer.common.util.CollectionUtils;
 import org.dbsyncer.manager.Manager;
 import org.dbsyncer.parser.logger.LogService;
 import org.dbsyncer.parser.logger.LogType;
+import org.dbsyncer.parser.model.Mapping;
 import org.dbsyncer.plugin.config.Plugin;
 import org.dbsyncer.plugin.enums.FileSuffixEnum;
+import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * @author AE86
@@ -28,8 +36,30 @@ public class PluginServiceImpl implements PluginService {
     private LogService logService;
 
     @Override
-    public List<Plugin> getPluginAll() {
-        return manager.getPluginAll();
+    public List<PluginVo> getPluginAll() {
+        List<Plugin> pluginAll = manager.getPluginAll();
+        List<PluginVo> vos = new ArrayList<>();
+        if (!CollectionUtils.isEmpty(pluginAll)) {
+            Map<String, List<String>> pluginClassNameMap = new HashMap<>();
+            List<Mapping> mappingAll = manager.getMappingAll();
+            if (!CollectionUtils.isEmpty(mappingAll)) {
+                mappingAll.forEach(mapping -> {
+                    Plugin plugin = mapping.getPlugin();
+                    if(null != plugin){
+                        pluginClassNameMap.putIfAbsent(plugin.getClassName(), new ArrayList<>());
+                        pluginClassNameMap.get(plugin.getClassName()).add(mapping.getName());
+                    }
+                });
+            }
+
+            vos.addAll(pluginAll.stream().map(plugin -> {
+                PluginVo vo = new PluginVo();
+                BeanUtils.copyProperties(plugin, vo);
+                vo.setMappingName(StringUtils.join(pluginClassNameMap.get(plugin.getClassName()), "|"));
+                return vo;
+            }).collect(Collectors.toList()));
+        }
+        return vos;
     }
 
     @Override
@@ -37,6 +67,11 @@ public class PluginServiceImpl implements PluginService {
         return manager.getPluginPath();
     }
 
+    @Override
+    public String getLibraryPath() {
+        return manager.getLibraryPath();
+    }
+
     @Override
     public void loadPlugins() {
         manager.loadPlugins();

+ 19 - 0
dbsyncer-biz/src/main/java/org/dbsyncer/biz/vo/PluginVo.java

@@ -0,0 +1,19 @@
+package org.dbsyncer.biz.vo;
+
+import org.dbsyncer.plugin.config.Plugin;
+
+public class PluginVo extends Plugin {
+
+    /**
+     * 正在使用插件的驱动名称,多个","拼接
+     */
+    private String mappingName;
+
+    public String getMappingName() {
+        return mappingName;
+    }
+
+    public void setMappingName(String mappingName) {
+        this.mappingName = mappingName;
+    }
+}

+ 1 - 2
dbsyncer-cache/pom.xml

@@ -4,10 +4,9 @@
 	<parent>
 		<artifactId>dbsyncer</artifactId>
 		<groupId>org.ghi</groupId>
-		<version>1.0.9-Alpha</version>
+		<version>1.1.0-Alpha</version>
 	</parent>
 	<modelVersion>4.0.0</modelVersion>
-
 	<artifactId>dbsyncer-cache</artifactId>
 
 	<dependencies>

+ 1 - 2
dbsyncer-cluster/pom.xml

@@ -5,10 +5,9 @@
     <parent>
         <artifactId>dbsyncer</artifactId>
         <groupId>org.ghi</groupId>
-		<version>1.0.9-Alpha</version>
+		<version>1.1.0-Alpha</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
-
     <artifactId>dbsyncer-cluster</artifactId>
 
 </project>

+ 1 - 1
dbsyncer-common/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>dbsyncer</artifactId>
         <groupId>org.ghi</groupId>
-		<version>1.0.9-Alpha</version>
+		<version>1.1.0-Alpha</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <artifactId>dbsyncer-common</artifactId>

+ 1 - 1
dbsyncer-common/src/main/java/org/dbsyncer/common/spi/ConvertService.java

@@ -24,7 +24,7 @@ public interface ConvertService {
     /**
      * 增量同步
      *
-     * @param event  事件(新增INSERT/修改UPDATE/删除DELETE)
+     * @param event  事件(INSERT/UPDATE/DELETE)
      * @param source 数据源
      * @param target 目标源
      */

+ 1 - 2
dbsyncer-connector/pom.xml

@@ -5,10 +5,9 @@
     <parent>
         <artifactId>dbsyncer</artifactId>
         <groupId>org.ghi</groupId>
-		<version>1.0.9-Alpha</version>
+		<version>1.1.0-Alpha</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
-
     <artifactId>dbsyncer-connector</artifactId>
 
     <dependencies>

+ 1 - 2
dbsyncer-listener/pom.xml

@@ -5,10 +5,9 @@
     <parent>
         <artifactId>dbsyncer</artifactId>
         <groupId>org.ghi</groupId>
-		<version>1.0.9-Alpha</version>
+		<version>1.1.0-Alpha</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
-
     <artifactId>dbsyncer-listener</artifactId>
 
     <dependencies>

+ 6 - 1
dbsyncer-listener/src/main/java/org/dbsyncer/listener/oracle/dcn/DBChangeNotification.java

@@ -96,7 +96,12 @@ public class DBChangeNotification {
 
             // 配置监听表
             for (Map.Entry<Integer, String> m : tables.entrySet()) {
-                statement.executeQuery(String.format(QUERY_TABLE_SQL, m.getValue()));
+                String sql = String.format(QUERY_TABLE_SQL, m.getValue());
+                try {
+                    statement.executeQuery(sql);
+                } catch (SQLException e) {
+                    logger.debug("配置监听表异常:{}, {}", sql, e.getMessage());
+                }
             }
         } catch (SQLException ex) {
             // if an exception occurs, we need to close the registration in order

+ 1 - 2
dbsyncer-manager/pom.xml

@@ -5,10 +5,9 @@
     <parent>
         <artifactId>dbsyncer</artifactId>
         <groupId>org.ghi</groupId>
-		<version>1.0.9-Alpha</version>
+		<version>1.1.0-Alpha</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
-
     <artifactId>dbsyncer-manager</artifactId>
 
     <dependencies>

+ 2 - 0
dbsyncer-manager/src/main/java/org/dbsyncer/manager/Manager.java

@@ -128,5 +128,7 @@ public interface Manager extends Executor {
 
     String getPluginPath();
 
+    String getLibraryPath();
+
     void loadPlugins();
 }

+ 5 - 0
dbsyncer-manager/src/main/java/org/dbsyncer/manager/ManagerFactory.java

@@ -300,6 +300,11 @@ public class ManagerFactory implements Manager, ApplicationListener<ClosedEvent>
         return pluginFactory.getPluginPath();
     }
 
+    @Override
+    public String getLibraryPath() {
+        return pluginFactory.getLibraryPath();
+    }
+
     @Override
     public void loadPlugins() {
         pluginFactory.loadPlugins();

+ 1 - 2
dbsyncer-monitor/pom.xml

@@ -5,10 +5,9 @@
     <parent>
         <artifactId>dbsyncer</artifactId>
         <groupId>org.ghi</groupId>
-		<version>1.0.9-Alpha</version>
+		<version>1.1.0-Alpha</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
-
     <artifactId>dbsyncer-monitor</artifactId>
 
     <dependencies>

+ 1 - 2
dbsyncer-parser/pom.xml

@@ -5,10 +5,9 @@
     <parent>
         <artifactId>dbsyncer</artifactId>
         <groupId>org.ghi</groupId>
-		<version>1.0.9-Alpha</version>
+		<version>1.1.0-Alpha</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
-
     <artifactId>dbsyncer-parser</artifactId>
 
     <dependencies>

+ 1 - 2
dbsyncer-plugin/pom.xml

@@ -5,10 +5,9 @@
     <parent>
         <artifactId>dbsyncer</artifactId>
         <groupId>org.ghi</groupId>
-		<version>1.0.9-Alpha</version>
+		<version>1.1.0-Alpha</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
-
     <artifactId>dbsyncer-plugin</artifactId>
 
     <dependencies>

+ 39 - 8
dbsyncer-plugin/src/main/java/org/dbsyncer/plugin/PluginFactory.java

@@ -6,15 +6,17 @@ import org.dbsyncer.common.util.CollectionUtils;
 import org.dbsyncer.plugin.config.Plugin;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import javax.annotation.PostConstruct;
 import java.io.File;
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
 
 /**
  * @author AE86
@@ -32,19 +34,44 @@ public class PluginFactory {
     private final String PLUGIN_PATH = new StringBuilder(System.getProperty("user.dir")).append(File.separatorChar).append("plugins")
             .append(File.separatorChar).toString();
 
+    /**
+     * 依赖路径dbsyncer/lib/
+     */
+    private final String LIBRARY_PATH = new StringBuilder(System.getProperty("user.dir")).append(File.separatorChar).append("lib")
+            .append(File.separatorChar).toString();
+
     private final List<Plugin> plugins = new LinkedList<>();
 
-    private final Map<String, ConvertService> service = new ConcurrentHashMap<>();
+    @Autowired
+    private Map<String, ConvertService> service;
+
+    @PostConstruct
+    private void init() {
+        Map<String, ConvertService> unmodifiable = new LinkedHashMap<>();
+        if (!CollectionUtils.isEmpty(service)) {
+            service.forEach((k, s) -> {
+                String className = s.getClass().getName();
+                unmodifiable.putIfAbsent(className, s);
+                plugins.add(new Plugin(s.getName(), className, s.getVersion(), "", true));
+            });
+        }
 
-    public synchronized void loadPlugins() {
-        plugins.clear();
         service.clear();
+        service.putAll(unmodifiable);
+    }
+
+    public synchronized void loadPlugins() {
+        if (!CollectionUtils.isEmpty(plugins)) {
+            List<Plugin> unmodifiablePlugin = plugins.stream().filter(p -> p.isUnmodifiable()).collect(Collectors.toList());
+            plugins.clear();
+            plugins.addAll(unmodifiablePlugin);
+        }
         try {
             FileUtils.forceMkdir(new File(PLUGIN_PATH));
         } catch (IOException e) {
             logger.error(e.getMessage());
         }
-        Collection<File> files = FileUtils.listFiles(new File(PLUGIN_PATH), new String[] {"jar"}, true);
+        Collection<File> files = FileUtils.listFiles(new File(PLUGIN_PATH), new String[]{"jar"}, true);
         if (!CollectionUtils.isEmpty(files)) {
             files.forEach(f -> loadPlugin(f));
         }
@@ -55,8 +82,12 @@ public class PluginFactory {
         return PLUGIN_PATH;
     }
 
+    public String getLibraryPath() {
+        return LIBRARY_PATH;
+    }
+
     public List<Plugin> getPluginAll() {
-        return plugins;
+        return Collections.unmodifiableList(plugins);
     }
 
     public void convert(Plugin plugin, List<Map> source, List<Map> target) {
@@ -80,11 +111,11 @@ public class PluginFactory {
         try {
             String fileName = jar.getName();
             URL url = jar.toURI().toURL();
-            URLClassLoader loader = new URLClassLoader(new URL[] {url}, Thread.currentThread().getContextClassLoader());
+            URLClassLoader loader = new URLClassLoader(new URL[]{url}, Thread.currentThread().getContextClassLoader());
             ServiceLoader<ConvertService> services = ServiceLoader.load(ConvertService.class, loader);
             for (ConvertService s : services) {
                 String className = s.getClass().getName();
-                service.put(className, s);
+                service.putIfAbsent(className, s);
                 plugins.add(new Plugin(s.getName(), className, s.getVersion(), fileName));
             }
         } catch (MalformedURLException e) {

+ 16 - 0
dbsyncer-plugin/src/main/java/org/dbsyncer/plugin/config/Plugin.java

@@ -19,14 +19,22 @@ public class Plugin {
     // Jar名称
     private String fileName;
 
+    // 是否系统预置
+    private boolean unmodifiable;
+
     public Plugin() {
     }
 
     public Plugin(String name, String className, String version, String fileName) {
+        this(name, className, version, fileName, false);
+    }
+
+    public Plugin(String name, String className, String version, String fileName, boolean unmodifiable) {
         this.name = name;
         this.className = className;
         this.version = version;
         this.fileName = fileName;
+        this.unmodifiable = unmodifiable;
     }
 
     public String getName() {
@@ -63,4 +71,12 @@ public class Plugin {
         this.fileName = fileName;
         return this;
     }
+
+    public boolean isUnmodifiable() {
+        return unmodifiable;
+    }
+
+    public void setUnmodifiable(boolean unmodifiable) {
+        this.unmodifiable = unmodifiable;
+    }
 }

+ 40 - 0
dbsyncer-plugin/src/main/java/org/dbsyncer/plugin/service/DemoConvertServiceImpl.java

@@ -0,0 +1,40 @@
+package org.dbsyncer.plugin.service;
+
+import org.dbsyncer.common.spi.ConvertService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+@Component
+public class DemoConvertServiceImpl implements ConvertService {
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    /**
+     * 版本号
+     */
+    @Value(value = "${info.app.version}")
+    private String version;
+
+    @Override
+    public void convert(List<Map> source, List<Map> target) {
+    }
+
+    @Override
+    public void convert(String event, Map source, Map target) {
+        logger.info(String.format("插件正在处理同步数据,事件:%s,数据:%s", event, source));
+    }
+
+    @Override
+    public String getVersion() {
+        return version;
+    }
+
+    public String getName() {
+        return "Demo";
+    }
+}

+ 1 - 2
dbsyncer-storage/pom.xml

@@ -5,10 +5,9 @@
     <parent>
         <artifactId>dbsyncer</artifactId>
         <groupId>org.ghi</groupId>
-		<version>1.0.9-Alpha</version>
+		<version>1.1.0-Alpha</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
-
     <artifactId>dbsyncer-storage</artifactId>
 
     <dependencies>

+ 1 - 2
dbsyncer-web/pom.xml

@@ -5,10 +5,9 @@
     <parent>
         <artifactId>dbsyncer</artifactId>
         <groupId>org.ghi</groupId>
-		<version>1.0.9-Alpha</version>
+		<version>1.1.0-Alpha</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
-
     <artifactId>dbsyncer-web</artifactId>
 
     <dependencies>

+ 41 - 2
dbsyncer-web/src/main/java/org/dbsyncer/web/controller/upload/UploadController.java

@@ -1,18 +1,23 @@
 package org.dbsyncer.web.controller.upload;
 
 import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
 import org.dbsyncer.biz.PluginService;
 import org.dbsyncer.biz.vo.RestResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.multipart.MultipartFile;
 
-import java.io.File;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
 
 @Controller
 @RequestMapping("/upload")
@@ -20,16 +25,23 @@ public class UploadController {
 
     private final Logger logger = LoggerFactory.getLogger(getClass());
 
+    /**
+     * 版本号
+     */
+    @Value(value = "${info.app.version}")
+    private String version;
+
     @RequestMapping("")
     public String index(ModelMap model) {
         model.put("plugins", pluginService.getPluginAll());
+        model.put("version", version);
         return "upload/upload";
     }
 
     @Autowired
     private PluginService pluginService;
 
-    @RequestMapping(value = "/upload")
+    @PostMapping(value = "/upload")
     @ResponseBody
     public RestResult upload(MultipartFile[] files) {
         try {
@@ -55,4 +67,31 @@ public class UploadController {
         }
     }
 
+    @GetMapping("/download")
+    public void download(HttpServletResponse response) {
+        String fileName = String.format("dbsyncer-common-%s.jar", version);
+        response.setHeader("content-type", "application/octet-stream");
+        response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
+        response.setContentType("application/octet-stream");
+        BufferedInputStream bis = null;
+        OutputStream outputStream = null;
+        try {
+            outputStream = response.getOutputStream();
+            String filePath = pluginService.getLibraryPath();
+            bis = new BufferedInputStream(new FileInputStream(new File(filePath + fileName)));
+            byte[] buff = new byte[2048];
+            int read = bis.read(buff);
+            while (read != -1) {
+                outputStream.write(buff, 0, buff.length);
+                outputStream.flush();
+                read = bis.read(buff);
+            }
+        } catch (IOException e) {
+            logger.error(e.getMessage());
+        } finally {
+            IOUtils.closeQuietly(bis);
+            IOUtils.closeQuietly(outputStream);
+        }
+    }
+
 }

+ 2 - 2
dbsyncer-web/src/main/resources/application.properties

@@ -23,8 +23,8 @@ management.endpoints.web.base-path=/app
 management.endpoints.web.exposure.include=*
 management.endpoint.health.show-details=always
 info.app.name=DBSyncer
-info.app.version=1.0.9-Alpha
-info.app.copyright=&copy;2021 ${info.app.name}(${info.app.version})<br />Designed By <a href='https://gitee.com/ghi/dbsyncer' target='_blank' >AE86</a>
+info.app.version=1.1.0-Alpha
+info.app.copyright=&copy;2021 ${info.app.name}(${info.app.version})<footer>Designed By <a href='https://gitee.com/ghi/dbsyncer' target='_blank' >AE86</a></footer>
 
 #All < Trace < Debug < Info < Warn < Error < Fatal < OFF
 logging.level.root=info

+ 3 - 3
dbsyncer-web/src/main/resources/public/index/index.html

@@ -28,7 +28,7 @@
                                     <div class="col-md-1" th:each="c,state : ${connectors}">
                                         <div th:id="${c?.id}" class="jumbotron dbsyncer_block">
                                             <div class="row">
-                                                <img th:src="@{'/img/'+ ${c?.config?.connectorType} + '.png'}">
+                                                <img draggable="false" th:src="@{'/img/'+ ${c?.config?.connectorType} + '.png'}">
                                             </div>
                                             <div class="row dbsyncer_over_hidden">
                                                 <span th:text="${c?.name}" th:title="${c?.name}" />
@@ -81,7 +81,7 @@
                                                 <div class="col-md-5">
                                                     <div class="well well-lg">
                                                         <div class="col-md-4">
-                                                            <img th:src="@{'/img/'+ ${m?.sourceConnector?.config?.connectorType} + '.png'}">
+                                                            <img draggable="false" th:src="@{'/img/'+ ${m?.sourceConnector?.config?.connectorType} + '.png'}">
                                                         </div>
                                                         <div class="col-md-7 dbsyncer_over_hidden">
                                                             <span th:text="${m?.sourceConnector?.name}" th:title="${m?.sourceConnector?.name}"></span>
@@ -106,7 +106,7 @@
                                                 <div class="col-md-5">
                                                     <div class="well well-lg">
                                                         <div class="col-md-4">
-                                                            <img th:src="@{'/img/'+ ${m?.targetConnector?.config?.connectorType} + '.png'}">
+                                                            <img draggable="false" th:src="@{'/img/'+ ${m?.targetConnector?.config?.connectorType} + '.png'}">
                                                         </div>
                                                         <div class="col-md-7 dbsyncer_over_hidden">
                                                             <span th:text="${m?.targetConnector?.name}" th:title="${m?.targetConnector?.name}"></span>

+ 8 - 0
dbsyncer-web/src/main/resources/public/mapping/add.html

@@ -57,6 +57,14 @@
                                 </div>
                             </div>
 
+                            <!-- 匹配相似表 -->
+                            <div class="form-group">
+                                <div class="col-sm-2 text-right"><label>匹配相似表</label></div>
+                                <div class="col-sm-10">
+                                    <input id="autoMatchTableSelect" name="autoMatchTable" class="form-control" type="checkbox" />
+                                </div>
+                            </div>
+
                         </div>
                     </div>
                 </div>

+ 2 - 2
dbsyncer-web/src/main/resources/public/mapping/edit.html

@@ -27,7 +27,7 @@
                             <div class="col-md-4">
                                 <div class="row">
                                     <div class="col-md-4 text-right">
-                                        <img class="dbsyncer_img" th:src="@{'/img/'+ ${mapping?.sourceConnector?.config?.connectorType} + '.png'}">
+                                        <img draggable="false" class="dbsyncer_img" th:src="@{'/img/'+ ${mapping?.sourceConnector?.config?.connectorType} + '.png'}">
                                     </div>
                                     <div class="col-md-8">
                                         <p class="driver_break_word">[[${mapping?.sourceConnector?.name}]]</p>
@@ -45,7 +45,7 @@
                             <div class="col-md-4">
                                 <div class="row">
                                     <div class="col-md-4 text-right">
-                                        <img class="dbsyncer_img" th:src="@{'/img/'+ ${mapping?.targetConnector?.config?.connectorType} + '.png'}">
+                                        <img draggable="false" class="dbsyncer_img" th:src="@{'/img/'+ ${mapping?.targetConnector?.config?.connectorType} + '.png'}">
                                     </div>
                                     <div class="col-md-8">
                                         <p class="driver_break_word">[[${mapping?.targetConnector?.name}]]</p>

+ 2 - 2
dbsyncer-web/src/main/resources/public/mapping/editTableGroup.html

@@ -19,7 +19,7 @@
                             <div class="col-md-4">
                                 <div class="row">
                                     <div class="col-md-4 text-right">
-                                        <img class="dbsyncer_img" th:src="@{'/img/'+ ${mapping?.sourceConnector?.config?.connectorType} + '.png'}">
+                                        <img draggable="false" class="dbsyncer_img" th:src="@{'/img/'+ ${mapping?.sourceConnector?.config?.connectorType} + '.png'}">
                                     </div>
                                     <div class="col-md-8">
                                         <p class="driver_break_word">[[${mapping?.sourceConnector?.name}]]</p>
@@ -37,7 +37,7 @@
                             <div class="col-md-4">
                                 <div class="row">
                                     <div class="col-md-4 text-right">
-                                        <img class="dbsyncer_img" th:src="@{'/img/'+ ${mapping?.targetConnector?.config?.connectorType} + '.png'}">
+                                        <img draggable="false" class="dbsyncer_img" th:src="@{'/img/'+ ${mapping?.targetConnector?.config?.connectorType} + '.png'}">
                                     </div>
                                     <div class="col-md-8">
                                         <p class="driver_break_word">[[${mapping?.targetConnector?.name}]]</p>

+ 128 - 33
dbsyncer-web/src/main/resources/public/upload/upload.html

@@ -2,50 +2,141 @@
 <html xmlns="http://www.w3.org/1999/xhtml"
       xmlns:th="http://www.thymeleaf.org" lang="zh-CN">
 
-<div class="container">
-    <form id="uploadForm" class="form-horizontal" role="form">
+<div class="container-fluid">
+    <div class="row">
+        <div class="col-md-12">
+            <!-- 插件文档 -->
+            <div class="col-md-5">
+                <blockquote>
+                    <p>插件有什么用?</p>
+                    <small class="text-muted">插件是一种可扩展全量同步和增量同步实现数据转换的技术方式。通过插件可以接收同步数据,自定义同步到目标源的行数据,也能消费数据并实现更多业务场景。</small>
+                </blockquote>
 
-        <div class="page-header">
-            <h3>上传插件 <small>只支持 "jar" 的文件扩展名.</small></h3>
-        </div>
+                <p>如何开发插件?</p>
+                <ol>
+                    <li>新建java或maven工程,比如app</li>
+                    <li>导入开发包:
+                        <ul>
+                            <li>方式1:导入jar <a onClick="downLoad()" href="javascript:;" title="下载开发包">dbsyncer-common-[[${version}]].jar</a></li>
+                            <li>方式2:引入pom(需要安装到本地)
+                                <pre>&lt;dependency&gt;<br/>&nbsp;&nbsp;&lt;groupId>org.ghi&lt;/groupId&gt;<br/>&nbsp;&nbsp;&lt;artifactId>dbsyncer-common&lt;/artifactId&gt;<br/>&nbsp;&nbsp;&lt;version>[[${version}]]&lt;/version&gt;<br/>&lt;/dependency&gt;</pre>
+                            </li>
+                        </ul>
+                    </li>
+                    <li>
+                        <simple>新建一个类,比如MyPlugin,实现接口ConvertService方法</simple>
+<pre>package org.test;
+
+import org.dbsyncer.common.spi.ConvertService;
+
+import java.util.List;
+import java.util.Map;
+
+public class MyPlugin implements ConvertService{
+
+    /**
+    * 全量同步
+    *
+    * @param source 数据源
+    * @param target 目标源
+    */
+    @Override
+    public void convert(List&lt;Map&gt; source, List&lt;Map&gt; target) {
+        // TODO 消费或处理数据
+    }
+
+    /**
+    * 增量同步
+    *
+    * @param event  事件(INSERT/UPDATE/DELETE)
+    * @param source 数据源
+    * @param target 目标源
+    */
+    @Override
+    public void convert(String event, Map source, Map target) {
+        // TODO 消费或处理数据
+    }
 
-        <div class="form-group">
-            <div class="file-loading">
-                <input id="filePlugin" type="file" name="files" multiple="multiple" />
+    /**
+    * 重写方法:设置版本号
+    *
+    * @return
+    */
+    @Override
+    public String getVersion() {
+        return "1.0.0";
+    }
+
+    /**
+    * 重写方法:设置插件名称
+    *
+    * @return
+    */
+    @Override
+    public String getName() {
+        return "MyPlugin";
+    }
+}</pre>
+                    </li>
+                    <li>
+                        <simple>/META-INF/新建services文件夹,并在services下新建一个文件,命名为org.dbsyncer.common.spi.ConvertService,文件写入实现类路径org.test.MyPlugin,如果有多个实现就换行再写入</simple>
+                        <p><img draggable="false" th:src="@{'/img/plugin/spi.png'}"></p>
+                    </li>
+                    <li>
+                        <simple>打包jar</simple>
+                        <p><img draggable="false" th:src="@{'/img/plugin/jar.png'}"></p>
+                    </li>
+                </ol>
             </div>
-        </div>
 
-        <div class="form-group">
-            <table class="table table-hover">
-                <caption>插件列表([[${plugins?.size()} ?: 0]])</caption>
-                <thead>
-                <tr>
-                    <th>名称</th>
-                    <th>类名</th>
-                    <th>版本</th>
-                    <th>文件</th>
-                    <th>操作</th>
-                </tr>
-                </thead>
-                <tbody id="pluginList">
-                <tr th:id="${p?.name}" th:each="p,state : ${plugins}">
-                    <td th:text="${p?.name}"/>
-                    <td th:text="${p?.className}"/>
-                    <td th:text="${p?.version}"/>
-                    <td th:text="${p?.fileName}"/>
-                    <td><a th:id="${p?.name}" class='fa fa-remove fa-2x pluginDelete dbsyncer_pointer' title='暂不支持' aria-disabled="true"></a></td>
-                </tr>
-                </tbody>
-            </table>
+            <!-- 插件列表 -->
+            <div class="col-md-7">
+                <form id="uploadForm" class="form-horizontal" role="form">
+                    <div class="page-header">
+                        <h3>上传插件 <small>只支持 "jar" 的文件扩展名.</small></h3>
+                    </div>
+
+                    <div class="form-group">
+                        <div class="file-loading">
+                            <input id="filePlugin" type="file" name="files" multiple="multiple" />
+                        </div>
+                    </div>
+
+                    <div class="form-group">
+                        <table class="table table-hover">
+                            <caption>插件列表([[${plugins?.size()} ?: 0]])</caption>
+                            <thead>
+                            <tr>
+                                <th>名称</th>
+                                <th>运行驱动</th>
+                                <th>类名</th>
+                                <th>版本</th>
+                                <th>文件</th>
+                            </tr>
+                            </thead>
+                            <tbody id="pluginList">
+                            <tr th:id="${p?.name}" th:each="p,state : ${plugins}">
+                                <td th:title="内置插件" th:if="${p?.unmodifiable}"><i class="fa fa-plug fa_gray" aria-hidden="true"></i> [[${p?.name}]]</td>
+                                <td th:title="普通插件" th:if="${not p?.unmodifiable}"><i class="fa fa-star fa_blueviolet" aria-hidden="true"></i> [[${p?.name}]]</td>
+                                <td th:text="${p?.mappingName}"/>
+                                <td th:text="${p?.className}"/>
+                                <td th:text="${p?.version}"/>
+                                <td th:text="${p?.fileName}"/>
+                            </tr>
+                            </tbody>
+                        </table>
+                    </div>
+                </form>
+            </div>
         </div>
-    </form>
+    </div>
 </div>
 
 <script type="text/javascript">
     $("#filePlugin").fileinput({
         theme: 'fas',
         language: 'zh',
-        uploadUrl: '/upload/upload',
+        uploadUrl: $basePath + '/upload/upload',
         enctype: 'multipart/form-data',
         removeFromPreviewOnError:true, //当选择的文件不符合规则时,例如不是指定后缀文件、大小超出配置等,选择的文件不会出现在预览框中,只会显示错误信息
         allowedFileExtensions: ['jar'],
@@ -60,5 +151,9 @@
         }
         doLoader("/upload");
     });
+
+    function downLoad(){
+        window.open($basePath + "/upload/download");
+    }
 </script>
 </html>

+ 2 - 0
dbsyncer-web/src/main/resources/static/css/common.css

@@ -13,3 +13,5 @@
  * 强制单词换行
  */
 .driver_break_word { word-break: break-all;word-wrap:break-word;white-space:normal}
+.fa_gray {color: gray}
+.fa_blueviolet {color: blueviolet}

BIN
dbsyncer-web/src/main/resources/static/img/plugin/jar.png


BIN
dbsyncer-web/src/main/resources/static/img/plugin/spi.png


+ 12 - 0
dbsyncer-web/src/main/resources/static/js/mapping/add.js

@@ -9,6 +9,15 @@ function submit(data) {
     });
 }
 
+// 绑定匹配相似表复选框事件
+function bindAutoMatchTableCheckBoxClick(){
+    $('#autoMatchTableSelect').iCheck({
+        checkboxClass: 'icheckbox_square-blue',
+        labelHover: false,
+        cursor: true
+    });
+}
+
 $(function () {
     // 兼容IE PlaceHolder
     $('input[type="text"],input[type="password"],textarea').PlaceHolder();
@@ -19,6 +28,9 @@ $(function () {
         theme: "classic"
     });
 
+    // 绑定匹配相似表复选框事件
+    bindAutoMatchTableCheckBoxClick();
+
     //保存
     $("#mappingSubmitBtn").click(function () {
         var $form = $("#mappingAddForm");

+ 1 - 1
dbsyncer-web/src/main/resources/static/js/monitor/index.js

@@ -397,7 +397,7 @@ function showMetricTable(metrics){
     $.each(metrics, function(i) {
         html += '<tr>';
         html += '   <td style="width:5%;">'+ (i + 1) +'</td>';
-        html += '   <td>'+ metrics[i].metricName +'</td>';
+        html += '   <td>'+ '['+ metrics[i].group + ']' + metrics[i].metricName +'</td>';
         html += '   <td>'+ metrics[i].detail +'</td>';
         html += '</tr>';
     });

+ 1 - 1
pom.xml

@@ -6,7 +6,7 @@
 
     <groupId>org.ghi</groupId>
     <artifactId>dbsyncer</artifactId>
-	<version>1.0.9-Alpha</version>
+	<version>1.1.0-Alpha</version>
     <packaging>pom</packaging>
     <name>dbsyncer</name>
     <url>https://gitee.com/ghi/dbsyncer</url>

+ 1 - 1
version.cmd

@@ -1,6 +1,6 @@
 @echo off
 
-set APP_VERSION=1.0.9-Alpha
+set APP_VERSION=1.1.0-Alpha
 
 echo "Clean Project ..."
 call mvn clean -f pom.xml