소스 검색

!68 merge
Merge pull request !68 from AE86/fuxinpeng-beta

AE86 2 년 전
부모
커밋
5dc3559ffb
20개의 변경된 파일808개의 추가작업 그리고 41개의 파일을 삭제
  1. 54 0
      dbsyncer-biz/src/main/java/org/dbsyncer/biz/ProjectGroupService.java
  2. 79 0
      dbsyncer-biz/src/main/java/org/dbsyncer/biz/checker/impl/group/ProjectGroupChecker.java
  3. 119 0
      dbsyncer-biz/src/main/java/org/dbsyncer/biz/impl/ProjectGroupServiceImpl.java
  4. 35 0
      dbsyncer-biz/src/main/java/org/dbsyncer/biz/vo/ProjectGroupVo.java
  5. 12 1
      dbsyncer-manager/src/main/java/org/dbsyncer/manager/Manager.java
  6. 30 1
      dbsyncer-manager/src/main/java/org/dbsyncer/manager/ManagerFactory.java
  7. 4 4
      dbsyncer-manager/src/main/java/org/dbsyncer/manager/config/PreloadCallBack.java
  8. 11 1
      dbsyncer-manager/src/main/java/org/dbsyncer/manager/enums/HandlerEnum.java
  9. 3 2
      dbsyncer-manager/src/main/java/org/dbsyncer/manager/template/impl/PreloadTemplate.java
  10. 37 0
      dbsyncer-parser/src/main/java/org/dbsyncer/parser/model/ProjectGroup.java
  11. 2 1
      dbsyncer-storage/src/main/java/org/dbsyncer/storage/constant/ConfigConstant.java
  12. 12 12
      dbsyncer-web/src/main/java/org/dbsyncer/web/controller/index/IndexController.java
  13. 125 0
      dbsyncer-web/src/main/java/org/dbsyncer/web/controller/index/ProjectGroupController.java
  14. 78 0
      dbsyncer-web/src/main/resources/public/group/save.html
  15. 43 3
      dbsyncer-web/src/main/resources/public/index/index.html
  16. 6 10
      dbsyncer-web/src/main/resources/static/css/index/index.css
  17. 31 2
      dbsyncer-web/src/main/resources/static/js/common.js
  18. 53 0
      dbsyncer-web/src/main/resources/static/js/group/addOrEdit.js
  19. 73 1
      dbsyncer-web/src/main/resources/static/js/index/index.js
  20. 1 3
      dbsyncer-web/src/main/resources/static/plugins/js/loading-plus/loading-plus.js

+ 54 - 0
dbsyncer-biz/src/main/java/org/dbsyncer/biz/ProjectGroupService.java

@@ -0,0 +1,54 @@
+package org.dbsyncer.biz;
+
+import org.dbsyncer.biz.vo.ProjectGroupVo;
+import org.dbsyncer.parser.model.ProjectGroup;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 分组管理服务
+ *
+ * @author xinpeng.Fu
+ * @version 1.0.0
+ * @date 2022/6/9 17:09
+ **/
+public interface ProjectGroupService {
+
+    /**
+     * 新增分组
+     *
+     * @param params
+     */
+    String add(Map<String, String> params);
+
+    /**
+     * 修改分组
+     *
+     * @param params
+     */
+    String edit(Map<String, String> params);
+
+    /**
+     * 删除分组
+     *
+     * @param id
+     */
+    String remove(String id);
+
+    /**
+     * 获取分组
+     *
+     * @param id
+     * @return
+     */
+    ProjectGroupVo getProjectGroup(String id);
+
+    /**
+     * 获取所有分组
+     *
+     * @return
+     */
+    List<ProjectGroup> getProjectGroupAll();
+
+}

+ 79 - 0
dbsyncer-biz/src/main/java/org/dbsyncer/biz/checker/impl/group/ProjectGroupChecker.java

@@ -0,0 +1,79 @@
+package org.dbsyncer.biz.checker.impl.group;
+
+import org.dbsyncer.biz.checker.AbstractChecker;
+import org.dbsyncer.common.util.CollectionUtils;
+import org.dbsyncer.common.util.StringUtil;
+import org.dbsyncer.manager.Manager;
+import org.dbsyncer.parser.model.ConfigModel;
+import org.dbsyncer.parser.model.ProjectGroup;
+import org.dbsyncer.storage.constant.ConfigConstant;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.Assert;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * @author xinpeng.Fu
+ * @version 1.0.0
+ * @date 2022/6/9 17:09
+ **/
+@Component
+public class ProjectGroupChecker extends AbstractChecker {
+
+    @Autowired
+    private Manager manager;
+
+    /**
+     * 新增配置
+     *
+     * @param params
+     * @return
+     */
+    @Override
+    public ConfigModel checkAddConfigModel(Map<String, String> params) {
+        String name = params.get(ConfigConstant.CONFIG_MODEL_NAME);
+        ProjectGroup projectGroup = new ProjectGroup();
+        projectGroup.setType(ConfigConstant.PROJECT_GROUP);
+        projectGroup.setName(name);
+
+        modifyProjectGroup(projectGroup, params);
+
+        // 修改基本配置
+        this.modifyConfigModel(projectGroup, params);
+
+        return projectGroup;
+    }
+
+    /**
+     * 修改配置
+     *
+     * @param params
+     * @return
+     */
+    @Override
+    public ConfigModel checkEditConfigModel(Map<String, String> params) {
+        String id = params.get(ConfigConstant.CONFIG_MODEL_ID);
+        ProjectGroup projectGroup = manager.getProjectGroup(id);
+        Assert.notNull(projectGroup, "Can not find project group.");
+
+        modifyProjectGroup(projectGroup, params);
+
+        // 修改基本配置
+        this.modifyConfigModel(projectGroup, params);
+        return projectGroup;
+    }
+
+    private void modifyProjectGroup(ProjectGroup projectGroup, Map<String, String> params) {
+        String[] connectorIds = StringUtil.split(params.get("connectorIds"), "|");
+        String[] mappingIds = StringUtil.split(params.get("mappingIds"), "|");
+        boolean exist = (connectorIds != null && connectorIds.length > 0) | (mappingIds != null && mappingIds.length > 0);
+        Assert.isTrue(exist, "请选择连接或驱动.");
+
+        projectGroup.setConnectorIds(CollectionUtils.isEmpty(connectorIds) ? Collections.EMPTY_LIST : Arrays.asList(connectorIds));
+        projectGroup.setMappingIds(CollectionUtils.isEmpty(mappingIds) ? Collections.EMPTY_LIST : Arrays.asList(mappingIds));
+    }
+
+}

+ 119 - 0
dbsyncer-biz/src/main/java/org/dbsyncer/biz/impl/ProjectGroupServiceImpl.java

@@ -0,0 +1,119 @@
+package org.dbsyncer.biz.impl;
+
+import org.dbsyncer.biz.ConnectorService;
+import org.dbsyncer.biz.MappingService;
+import org.dbsyncer.biz.ProjectGroupService;
+import org.dbsyncer.biz.checker.Checker;
+import org.dbsyncer.biz.vo.MappingVo;
+import org.dbsyncer.biz.vo.ProjectGroupVo;
+import org.dbsyncer.common.util.CollectionUtils;
+import org.dbsyncer.common.util.StringUtil;
+import org.dbsyncer.manager.Manager;
+import org.dbsyncer.parser.logger.LogType;
+import org.dbsyncer.parser.model.ConfigModel;
+import org.dbsyncer.parser.model.Connector;
+import org.dbsyncer.parser.model.ProjectGroup;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 分组
+ *
+ * @author xinpeng.Fu
+ * @version 1.0.0
+ * @date 2022/6/9 17:09
+ **/
+@Service
+public class ProjectGroupServiceImpl extends BaseServiceImpl implements ProjectGroupService {
+
+    @Autowired
+    private ConnectorService connectorService;
+
+    @Autowired
+    private MappingService mappingService;
+
+    @Autowired
+    private Manager manager;
+
+    @Autowired
+    private Checker projectGroupChecker;
+
+    @Override
+    public String add(Map<String, String> params) {
+        ConfigModel model = projectGroupChecker.checkAddConfigModel(params);
+        log(LogType.ConnectorLog.INSERT, model);
+
+        return manager.addProjectGroup(model);
+    }
+
+    @Override
+    public String edit(Map<String, String> params) {
+        ConfigModel model = projectGroupChecker.checkEditConfigModel(params);
+        log(LogType.ConnectorLog.UPDATE, model);
+
+        return manager.editProjectGroup(model);
+    }
+
+    @Override
+    public String remove(String id) {
+        ProjectGroup projectGroup = manager.getProjectGroup(id);
+        log(LogType.ConnectorLog.DELETE, projectGroup);
+        Assert.notNull(projectGroup, "该分组已被删除");
+        manager.removeProjectGroup(id);
+        return "删除分组成功!";
+    }
+
+    @Override
+    public ProjectGroupVo getProjectGroup(String id) {
+        ProjectGroupVo vo = new ProjectGroupVo();
+        if (StringUtil.isBlank(id)) {
+            vo.setConnectors(connectorService.getConnectorAll());
+            vo.setMappings(mappingService.getMappingAll());
+            return vo;
+        }
+
+        ProjectGroup projectGroup = manager.getProjectGroup(id);
+        Assert.notNull(projectGroup, "该分组已被删除");
+        BeanUtils.copyProperties(projectGroup, vo);
+        vo.setConnectors(Collections.EMPTY_LIST);
+        vo.setMappings(Collections.EMPTY_LIST);
+
+        // 过滤连接器
+        List<String> connectorIds = projectGroup.getConnectorIds();
+        if (!CollectionUtils.isEmpty(connectorIds)) {
+            Set<String> connectorIdSet = new HashSet<>(connectorIds);
+            List<Connector> connectors = connectorService.getConnectorAll();
+            if (!CollectionUtils.isEmpty(connectors)) {
+                vo.setConnectors(connectors.stream()
+                        .filter((connector -> connectorIdSet.contains(connector.getId())))
+                        .collect(Collectors.toList())
+                );
+            }
+        }
+
+        // 过滤驱动
+        List<String> mappingIds = projectGroup.getMappingIds();
+        if (!CollectionUtils.isEmpty(mappingIds)) {
+            Set<String> mappingIdSet = new HashSet<>(mappingIds);
+            List<MappingVo> mappings = mappingService.getMappingAll();
+            if (!CollectionUtils.isEmpty(mappings)) {
+                vo.setMappings(mappings.stream()
+                        .filter((mapping -> mappingIdSet.contains(mapping.getId())))
+                        .collect(Collectors.toList())
+                );
+            }
+        }
+        return vo;
+    }
+
+    @Override
+    public List<ProjectGroup> getProjectGroupAll() {
+        return manager.getProjectGroupAll();
+    }
+
+}

+ 35 - 0
dbsyncer-biz/src/main/java/org/dbsyncer/biz/vo/ProjectGroupVo.java

@@ -0,0 +1,35 @@
+package org.dbsyncer.biz.vo;
+
+import org.dbsyncer.parser.model.Connector;
+import org.dbsyncer.parser.model.ProjectGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author AE86
+ * @version 1.0.0
+ * @date 2022/7/18 0:25
+ */
+public class ProjectGroupVo extends ProjectGroup {
+
+    private List<Connector> connectors = new ArrayList<>();
+
+    private List<MappingVo> mappings = new ArrayList<>();
+
+    public List<Connector> getConnectors() {
+        return connectors;
+    }
+
+    public void setConnectors(List<Connector> connectors) {
+        this.connectors = connectors;
+    }
+
+    public List<MappingVo> getMappings() {
+        return mappings;
+    }
+
+    public void setMappings(List<MappingVo> mappings) {
+        this.mappings = mappings;
+    }
+}

+ 12 - 1
dbsyncer-manager/src/main/java/org/dbsyncer/manager/Manager.java

@@ -27,6 +27,17 @@ import java.util.Map;
  */
 public interface Manager extends Executor {
 
+    // project group
+    String addProjectGroup(ConfigModel model);
+
+    String editProjectGroup(ConfigModel model);
+
+    ProjectGroup getProjectGroup(String id);
+
+    void removeProjectGroup(String id);
+
+    List<ProjectGroup> getProjectGroupAll();
+
     // Connector
     ConnectorMapper connect(ConnectorConfig config);
 
@@ -132,4 +143,4 @@ public interface Manager extends Executor {
     String getLibraryPath();
 
     void loadPlugins();
-}
+}

+ 30 - 1
dbsyncer-manager/src/main/java/org/dbsyncer/manager/ManagerFactory.java

@@ -66,6 +66,35 @@ public class ManagerFactory implements Manager, ApplicationListener<ClosedEvent>
     @Autowired
     private Map<String, Puller> map;
 
+    @Override
+    public String addProjectGroup(ConfigModel model) {
+        return operationTemplate.execute(new OperationConfig(model, HandlerEnum.OPR_ADD));
+    }
+
+    @Override
+    public String editProjectGroup(ConfigModel model) {
+        return operationTemplate.execute(new OperationConfig(model, HandlerEnum.OPR_EDIT));
+    }
+
+    @Override
+    public ProjectGroup getProjectGroup(String id) {
+        return operationTemplate.queryObject(ProjectGroup.class, id);
+    }
+
+    @Override
+    public void removeProjectGroup(String id) {
+        operationTemplate.remove(new OperationConfig(id));
+    }
+
+    @Override
+    public List<ProjectGroup> getProjectGroupAll() {
+        ProjectGroup projectGroup = new ProjectGroup();
+        projectGroup.setType(ConfigConstant.PROJECT_GROUP);
+        QueryConfig<ProjectGroup> queryConfig = new QueryConfig<>(projectGroup);
+        List<ProjectGroup> groups = operationTemplate.queryAll(queryConfig);
+        return groups;
+    }
+
     @Override
     public ConnectorMapper connect(ConnectorConfig config) {
         return parser.connect(config);
@@ -383,4 +412,4 @@ public class ManagerFactory implements Manager, ApplicationListener<ClosedEvent>
         return puller;
     }
 
-}
+}

+ 4 - 4
dbsyncer-manager/src/main/java/org/dbsyncer/manager/config/PreloadCallBack.java

@@ -2,10 +2,7 @@ package org.dbsyncer.manager.config;
 
 import org.dbsyncer.manager.template.Callback;
 import org.dbsyncer.parser.Parser;
-import org.dbsyncer.parser.model.Config;
-import org.dbsyncer.parser.model.Mapping;
-import org.dbsyncer.parser.model.Meta;
-import org.dbsyncer.parser.model.TableGroup;
+import org.dbsyncer.parser.model.*;
 
 public class PreloadCallBack implements Callback {
 
@@ -38,4 +35,7 @@ public class PreloadCallBack implements Callback {
         return parser.parseObject(json, Config.class);
     }
 
+    public Object parseProjectGroup() {
+        return parser.parseObject(json, ProjectGroup.class);
+    }
 }

+ 11 - 1
dbsyncer-manager/src/main/java/org/dbsyncer/manager/enums/HandlerEnum.java

@@ -82,6 +82,16 @@ public enum HandlerEnum {
         protected Object preload(PreloadCallBack preloadCallBack) {
             return preloadCallBack.parseConfig();
         }
+    }),
+
+    /**
+     * 预加载ProjectGroup
+     */
+    PRELOAD_PROJECT_GROUP(ConfigConstant.PROJECT_GROUP, true, new AbstractPreloadHandler(){
+        @Override
+        protected Object preload(PreloadCallBack preloadCallBack) {
+            return preloadCallBack.parseProjectGroup();
+        }
     });
 
     private String modelType;
@@ -119,4 +129,4 @@ public enum HandlerEnum {
     public GroupStrategyEnum getGroupStrategyEnum() {
         return groupStrategyEnum;
     }
-}
+}

+ 3 - 2
dbsyncer-manager/src/main/java/org/dbsyncer/manager/template/impl/PreloadTemplate.java

@@ -101,7 +101,8 @@ public final class PreloadTemplate implements ApplicationListener<ContextRefresh
         reload(map, HandlerEnum.PRELOAD_MAPPING);
         // Load metas
         reload(map, HandlerEnum.PRELOAD_META);
-
+        // Load projectGroups
+        reload(map, HandlerEnum.PRELOAD_PROJECT_GROUP);
         launch();
     }
 
@@ -167,4 +168,4 @@ public final class PreloadTemplate implements ApplicationListener<ContextRefresh
         launch();
     }
 
-}
+}

+ 37 - 0
dbsyncer-parser/src/main/java/org/dbsyncer/parser/model/ProjectGroup.java

@@ -0,0 +1,37 @@
+package org.dbsyncer.parser.model;
+
+import java.util.List;
+
+/**
+ * @author xinpeng.Fu
+ * @version 1.0.0
+ * @date 2022/6/9 17:09
+ **/
+public class ProjectGroup extends ConfigModel {
+
+    /**
+     * 连接器ID列表
+     */
+    private List<String> connectorIds;
+
+    /**
+     * 驱动ID列表
+     */
+    private List<String> mappingIds;
+
+    public List<String> getConnectorIds() {
+        return connectorIds;
+    }
+
+    public void setConnectorIds(List<String> connectorIds) {
+        this.connectorIds = connectorIds;
+    }
+
+    public List<String> getMappingIds() {
+        return mappingIds;
+    }
+
+    public void setMappingIds(List<String> mappingIds) {
+        this.mappingIds = mappingIds;
+    }
+}

+ 2 - 1
dbsyncer-storage/src/main/java/org/dbsyncer/storage/constant/ConfigConstant.java

@@ -25,6 +25,7 @@ public class ConfigConstant {
     public static final String TABLE_GROUP = "tableGroup";
     public static final String META = "meta";
     public static final String CONFIG = "config";
+    public static final String PROJECT_GROUP = "projectGroup";
 
     /**
      * 数据
@@ -33,4 +34,4 @@ public class ConfigConstant {
     public static final String DATA_EVENT = "event";
     public static final String DATA_ERROR = "error";
 
-}
+}

+ 12 - 12
dbsyncer-web/src/main/java/org/dbsyncer/web/controller/index/IndexController.java

@@ -1,7 +1,7 @@
 package org.dbsyncer.web.controller.index;
 
-import org.dbsyncer.biz.ConnectorService;
-import org.dbsyncer.biz.MappingService;
+import org.dbsyncer.biz.ProjectGroupService;
+import org.dbsyncer.biz.vo.ProjectGroupVo;
 import org.dbsyncer.biz.vo.RestResult;
 import org.dbsyncer.biz.vo.VersionVo;
 import org.dbsyncer.common.config.AppConfig;
@@ -13,32 +13,32 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
 
 import javax.servlet.http.HttpServletRequest;
-import java.io.UnsupportedEncodingException;
 
 @Controller
 @RequestMapping("/index")
 public class IndexController {
 
     @Autowired
-    private ConnectorService connectorService;
-
-    @Autowired
-    private MappingService mappingService;
+    private ProjectGroupService projectGroupService;
 
     @Autowired
     private AppConfig appConfig;
 
     @GetMapping("")
-    public String index(HttpServletRequest request, ModelMap model) {
-        model.put("connectors", connectorService.getConnectorAll());
-        model.put("mappings", mappingService.getMappingAll());
+    public String index(HttpServletRequest request, ModelMap model, String projectGroupId) {
+        ProjectGroupVo projectGroup = projectGroupService.getProjectGroup(projectGroupId);
+        model.put("connectors", projectGroup.getConnectors());
+        model.put("mappings", projectGroup.getMappings());
+        model.put("projectGroupId", projectGroupId);
+        model.put("projectGroups", projectGroupService.getProjectGroupAll());
         return "index/index.html";
     }
 
     @GetMapping("/version.json")
     @ResponseBody
-    public RestResult version() throws UnsupportedEncodingException {
+    public RestResult version() {
         return RestResult.restSuccess(new VersionVo(appConfig.getName(), appConfig.getCopyright()));
     }
 
-}
+}
+

+ 125 - 0
dbsyncer-web/src/main/java/org/dbsyncer/web/controller/index/ProjectGroupController.java

@@ -0,0 +1,125 @@
+package org.dbsyncer.web.controller.index;
+
+import org.dbsyncer.biz.ConnectorService;
+import org.dbsyncer.biz.MappingService;
+import org.dbsyncer.biz.ProjectGroupService;
+import org.dbsyncer.biz.vo.ProjectGroupVo;
+import org.dbsyncer.biz.vo.RestResult;
+import org.dbsyncer.web.controller.BaseController;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.ModelMap;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Map;
+
+/**
+ * 分组控制器
+ *
+ * @author xinpeng.Fu
+ * @date 2022/6/23 14:59
+ **/
+@Controller
+@RequestMapping("/projectGroup")
+public class ProjectGroupController extends BaseController {
+
+    private final Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Autowired
+    private ProjectGroupService projectGroupService;
+
+    @Autowired
+    private ConnectorService connectorService;
+
+    @Autowired
+    private MappingService mappingService;
+
+    @GetMapping("/page/add")
+    public String pageAdd(HttpServletRequest request, ModelMap model) {
+        model.put("connectors", connectorService.getConnectorAll());
+        model.put("mappings", mappingService.getMappingAll());
+        return "group/save";
+    }
+
+    @GetMapping("/page/edit")
+    public String pageEdit(HttpServletRequest request, ModelMap model, String id) {
+        model.put("projectGroup", projectGroupService.getProjectGroup(id));
+        model.put("connectors", connectorService.getConnectorAll());
+        model.put("mappings", mappingService.getMappingAll());
+        return "group/save";
+    }
+
+    /**
+     * 参数: name(必) mappingIds connectorIds
+     *
+     * @param request
+     * @return org.dbsyncer.biz.vo.RestResult
+     * @author xinpeng.Fu
+     * @date 2022/6/15 16:10
+     **/
+    @PostMapping("/add")
+    @ResponseBody
+    public RestResult add(HttpServletRequest request) {
+        try {
+            Map<String, String> params = getParams(request);
+            return RestResult.restSuccess(projectGroupService.add(params));
+        } catch (Exception e) {
+            logger.error(e.getLocalizedMessage(), e.getClass());
+            return RestResult.restFail(e.getMessage());
+        }
+    }
+
+    /**
+     * 参数: id(必) name(必) mappingIds connectorIds
+     *
+     * @param request
+     * @return org.dbsyncer.biz.vo.RestResult
+     * @author xinpeng.Fu
+     * @date 2022/6/15 16:10
+     **/
+    @PostMapping("/edit")
+    @ResponseBody
+    public RestResult edit(HttpServletRequest request) {
+        try {
+            Map<String, String> params = getParams(request);
+            return RestResult.restSuccess(projectGroupService.edit(params));
+        } catch (Exception e) {
+            logger.error(e.getLocalizedMessage(), e.getClass());
+            return RestResult.restFail(e.getMessage());
+        }
+    }
+
+    /**
+     * 参数: id(必)
+     *
+     * @param request
+     * @return org.dbsyncer.biz.vo.RestResult
+     * @author xinpeng.Fu
+     * @date 2022/6/15 16:10
+     **/
+    @PostMapping("/remove")
+    @ResponseBody
+    public RestResult remove(HttpServletRequest request, @RequestParam(value = "id") String id) {
+        try {
+            return RestResult.restSuccess(projectGroupService.remove(id));
+        } catch (Exception e) {
+            logger.error(e.getLocalizedMessage(), e.getClass());
+            return RestResult.restFail(e.getMessage());
+        }
+    }
+
+    @GetMapping("/getAll")
+    @ResponseBody
+    public RestResult getAll(HttpServletRequest request) {
+        try {
+            return RestResult.restSuccess(projectGroupService.getProjectGroupAll());
+        } catch (Exception e) {
+            logger.error(e.getLocalizedMessage(), e.getClass());
+            return RestResult.restFail(e.getMessage());
+        }
+    }
+
+}

+ 78 - 0
dbsyncer-web/src/main/resources/public/group/save.html

@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+xmlns:th="http://www.thymeleaf.org" lang="zh-CN">
+
+<div class="container-fluid">
+  <div class="container">
+    <form id="projectGroupAddForm" class="form-horizontal" role="form" method="post">
+      <!-- 标题 -->
+      <div class="row text-center">
+        <div class="page-header">
+          <h3>添加分组</h3>
+        </div>
+      </div>
+
+      <!-- 操作 -->
+      <div class="form-group">
+        <div class="col-md-10"></div>
+        <div class="col-md-2 text-right">
+          <button id="projectGroupSubmitBtn" type="button" class="btn btn-primary">
+            <span class="fa fa-save"></span>保存
+          </button>
+          <button id="projectGroupBackBtn" type="button" class="btn btn-default">
+            <span class="fa fa-reply"></span>返回
+          </button>
+        </div>
+      </div>
+
+      <!-- 配置 -->
+      <div class="row">
+        <div class="col-md-12">
+          <div class="panel panel-info">
+            <div class="panel-heading">
+              <h3 class="panel-title">分组配置</h3>
+            </div>
+
+            <div class="panel-body">
+              <!-- 名称 -->
+              <div class="form-group">
+                <label class="col-sm-2 control-label">名称 <strong class="driverVerifcateRequired">*</strong></label>
+                <div class="col-sm-10">
+                  <input type="hidden" name="id" id="id" th:value="${projectGroup?.id}">
+                  <input class="form-control" name="name" type="text" maxlength="50" dbsyncer-valid="require" placeholder="名称" th:value="${projectGroup?.name}"/>
+                </div>
+              </div>
+
+              <!-- 选择连接 -->
+              <div class="form-group">
+                <label class="col-sm-2 control-label">连接</label>
+                <div class="col-sm-10">
+                  <select id="connectorIds" name="connectorIds" class="form-control select-control-table" multiple="multiple">
+                    <option th:each="c,s:${connectors}" th:value="${c?.id}" th:text="${c?.name}" />
+                  </select>
+                  <input id="selectedConnectorIds" type="hidden" th:value="${#strings.listJoin(projectGroup?.connectorIds,',')}"/>
+                </div>
+              </div>
+
+              <!-- 选择驱动 -->
+              <div class="form-group">
+                <label class="col-sm-2 control-label">驱动</label>
+                <div class="col-sm-10">
+                  <select id="mappingIds" name="mappingIds" class="form-control select-control-table" multiple="multiple">
+                    <option th:each="m,s:${mappings}" th:value="${m?.id}" th:text="${m?.name}" />
+                  </select>
+                  <input id="selectedMappingIds" type="hidden" th:value="${#strings.listJoin(projectGroup?.mappingIds,',')}"/>
+                </div>
+              </div>
+
+            </div>
+          </div>
+        </div>
+      </div>
+
+    </form>
+  </div>
+</div>
+
+<script th:src="@{/js/group/addOrEdit.js}"></script>
+</html>

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

@@ -2,10 +2,50 @@
 <html xmlns="http://www.w3.org/1999/xhtml"
       xmlns:th="http://www.thymeleaf.org" lang="zh-CN">
 
-<!-- Connector Mapping -->
+<!-- ProjectGroup Connector Mapping -->
 <div class="container-fluid">
     <div class="row">
         <form class="form-horizontal" role="form" method="post">
+            <!-- 分组管理 -->
+            <div class="col-md-12">
+                <!-- 分组开始位置 -->
+                <div class="form-group">
+                    <div class="col-md-12">
+                        <button type="button" class="btn btn-primary" id="addProjectGroupBtn">
+                            <span class="fa fa-plus"></span>添加分组([[${projectGroups?.size()} ?: 0]])
+                        </button>
+                    </div>
+                </div>
+                <!-- 显示分组 -->
+                <div class="row" th:if="${projectGroups?.size() gt 0}">
+                    <div class="col-md-12">
+                        <div class="panel panel-default">
+                            <div class="panel-body">
+                                <div class="row">
+                                    <div class="col-md-4">
+                                        <select id="projectGroup" name="projectGroup"
+                                                class="form-control select-control">
+                                            <option value="" th:text="全部" selected/>
+                                            <option th:each="g,s:${projectGroups}" th:value="${g?.id}" th:text="${g?.name}" th:selected="${g?.id eq projectGroupId}"/>
+                                        </select>
+                                    </div>
+                                    <div class="col-md-6"></div>
+                                    <div class="col-md-2 text-right">
+                                        <div th:if="${not #strings.isEmpty(projectGroupId) }">
+                                            <button type="button" class="btn btn-primary" id="editProjectGroupBtn">
+                                                <span class="fa fa-pencil"></span>修改
+                                            </button>
+                                            <button type="button" class="btn btn-default" id="removeProjectGroupBtn">
+                                                <span class="fa fa-times"></span>删除
+                                            </button>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
 
             <!-- 连接管理 -->
             <div class="col-md-12">
@@ -72,7 +112,7 @@
                                 <div class="row mappingList">
                                     <!-- 驱动__开始 -->
                                     <div class="col-md-4" th:each="m,state : ${mappings}">
-                                        <div th:id="${m?.id}" class="jumbotron dbsyncer_block">
+                                        <div th:id="${m?.id}" th:class="${m?.meta?.model eq '全量'} ? 'jumbotron dbsyncer_block dbsyncer_block_full' : 'jumbotron dbsyncer_block dbsyncer_block_increment'">
                                             <!--驱动标题信息 -->
                                             <div class="row text-center" th:text="${m?.name}" th:title="${m?.name}"></div>
 
@@ -164,7 +204,7 @@
                                                 <!-- 运行中 -->
                                                 <li th:if="${m?.meta?.state eq 1}" th:url="'/mapping/stop?id='+${m?.id}"><a href="javascript:;"><i class="fa fa-times-circle-o well-sign-red"></i>&nbsp;&nbsp;停止</a></li>
                                                 <!-- 未运行 -->
-                                                <li th:if="${m?.meta?.state ne 1}" th:url="'/mapping/remove?id='+${m?.id}" confirm="true" confirmMessage="确认删除?"><a href="javascript:;"><i class="fa fa-trash well-sign-red"></i>&nbsp;&nbsp;删除</a></li>
+                                                <li th:if="${m?.meta?.state ne 1}" th:url="'/mapping/remove?id='+${m?.id}" confirm="true" confirmMessage="确认删除驱动?"><a href="javascript:;"><i class="fa fa-trash well-sign-red"></i>&nbsp;&nbsp;删除</a></li>
                                             </ul>
                                         </div>
                                     </div>

+ 6 - 10
dbsyncer-web/src/main/resources/static/css/index/index.css

@@ -1,13 +1,6 @@
 @charset "UTF-8";
-.connectorList img {
-    width: 65px;
-    height: 65px;
-    border-radius: 20%;
-}
-
-.connectorList .well-sign-red {
-    color: #ff0000;
-}
+.connectorList img{width: 65px;height: 65px; border-radius: 20%;}
+.connectorList .well-sign-red{color: #ff0000;}
 .connectorList .well-sign-operation{color: #999999; border-radius: 50%; }
 .connectorList .well-sign-operation:hover {color: #333333;}
 .connectorList .dropdown {position: absolute; right:16px; top: 0px;}
@@ -19,7 +12,7 @@
 .mappingList .jumbotron .line .running-state{position: absolute;left: calc(50% - 22px);;top: 45px;font-weight: bold;}
 .mappingList .jumbotron .well-sign-left{position: absolute;top: -10px;left: 3px;}
 .mappingList .jumbotron .well-sign-right{position: absolute;top: -31px;left: -7px;}
-.mappingList .well { word-wrap: break-word;word-break: normal;overflow:hidden;}
+.mappingList .well {word-wrap: break-word;word-break: normal;overflow:hidden;}
 .mappingList .well img{float:left;padding:8px 2px; border-radius: 20%;}
 .mappingList .well span{float:left;margin:21px 10px;}
 .mappingList .well-sign-green{color: #808040;}
@@ -29,3 +22,6 @@
 .mappingList .dropdown {position: absolute; right:0px; top: 0px;}
 .mappingList .dropdown-menu {right: 12px;left: auto; top: 28px;}
 .mappingList .dropdown-menu i{font-size: 20px;}
+
+.dbsyncer_block_increment { background: -webkit-linear-gradient(left, #ffffff, #dffbdc);}
+.dbsyncer_block_full { background: -webkit-linear-gradient(left, #ffffff, #dcf5fb);}

+ 31 - 2
dbsyncer-web/src/main/resources/static/js/common.js

@@ -13,7 +13,7 @@ var timer;
 function bootGrowl(data, type) {
     $.bootstrapGrowl(data, { // data为提示信息
         type: type == undefined ? 'success' : type,// type指提示类型
-        delay: 1000,// 提示框显示时间
+        delay: 3000,// 提示框显示时间
         allow_dismiss: true // 显示取消提示框
     });
 }
@@ -88,7 +88,12 @@ $.fn.serializeJson = function () {
 // 全局加载页面
 function doLoader(url){
     // 加载页面
-    $initContainer.load($basePath + url);
+    $initContainer.load($basePath + url, function (response, status, xhr) {
+        if (status != 'success') {
+            bootGrowl(response);
+        }
+        $.loadingT(false);
+    });
 }
 
 // 异常请求
@@ -136,4 +141,28 @@ function doGetter(url, params, action, loading) {
 // 全局Ajax get, 不显示加载动画
 function doGetWithoutLoading(url, params, action) {
     doGetter(url, params, action, false);
+}
+
+/**
+ * 判断字符串是否为空串
+ * @eg undefined true
+ * @eg null true
+ * @eg '' true
+ * @eg ' ' true
+ * @eg '1' false
+ * @return Boolean
+ */
+function isBlank(str) {
+    return str === undefined || str === null || str === false || str.length === 0;
+}
+
+/**
+ * 按照指定分隔符切分字符串
+ *
+ * @param str 带切分字符
+ * @param delimiter 分隔符
+ * @return Array
+ */
+function splitStrByDelimiter(str, delimiter) {
+    return isBlank(str) ? [] : str.split(delimiter);
 }

+ 53 - 0
dbsyncer-web/src/main/resources/static/js/group/addOrEdit.js

@@ -0,0 +1,53 @@
+/**
+ *
+ * Created by zhichao.qin on 2022/6/15
+ */
+function submit(data) {
+    if (data["id"]) {
+        doPoster("/projectGroup/edit", data, function (data) {
+            if (data.success == true) {
+                bootGrowl("修改分组成功!", "success");
+                backIndexPage();
+            } else {
+                bootGrowl(data.resultValue, "danger");
+            }
+        });
+    } else {
+        doPoster("/projectGroup/add", data, function (data) {
+            if (data.success == true) {
+                bootGrowl("新增分组成功!", "success");
+                backIndexPage();
+            } else {
+                bootGrowl(data.resultValue, "danger");
+            }
+        });
+    }
+}
+
+$(function () {
+    // 初始化select插件
+    initSelect($(".select-control-table"));
+
+    $("#connectorIds").selectpicker('val', splitStrByDelimiter($("#selectedConnectorIds").val(), ","));
+    $("#mappingIds").selectpicker('val', splitStrByDelimiter($("#selectedMappingIds").val(), ","));
+
+    //保存
+    $("#projectGroupSubmitBtn").click(function () {
+        var $form = $("#projectGroupAddForm");
+        if ($form.formValidate() == true) {
+            var data = $form.serializeJson();
+            if (data.connectorIds instanceof Array) {
+                data.connectorIds = data.connectorIds.join('|');
+            }
+            if (data.mappingIds instanceof Array) {
+                data.mappingIds = data.mappingIds.join('|');
+            }
+            submit(data);
+        }
+    });
+
+    //返回
+    $("#projectGroupBackBtn").click(function () {
+        backIndexPage();
+    });
+})

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

@@ -1,3 +1,68 @@
+// 添加分组
+function bindAddProjectGroup() {
+    $("#addProjectGroupBtn").click(function () {
+        doLoader("/projectGroup/page/add");
+    });
+}
+
+// 修改分组
+function bindEditProjectGroup($projectGroupSelect) {
+    $("#editProjectGroupBtn").click(function () {
+        var $id = $projectGroupSelect.selectpicker('val');
+        if (!isBlank($id)) {
+            doLoader('/projectGroup/page/edit?id=' + $id);
+            return;
+        }
+        bootGrowl("请选择分组", "danger");
+    });
+}
+
+// 删除分组
+function bindRemoveProjectGroup($projectGroupSelect) {
+    $("#removeProjectGroupBtn").click(function () {
+        var $id = $projectGroupSelect.selectpicker('val');
+        if (isBlank($id)) {
+            bootGrowl("请选择分组", "danger");
+            return;
+        }
+        BootstrapDialog.show({
+            title: "提示",
+            type: BootstrapDialog.TYPE_INFO,
+            message: "确认删除分组?",
+            size: BootstrapDialog.SIZE_NORMAL,
+            buttons: [{
+                label: "确定",
+                action: function (dialog) {
+                    doPoster('/projectGroup/remove',{id: $id}, function (data) {
+                        if (data.success == true) {
+                            // 显示主页
+                            backIndexPage();
+                            bootGrowl(data.resultValue, "success");
+                        } else {
+                            bootGrowl(data.resultValue, "danger");
+                        }
+                    });
+                    dialog.close();
+                }
+            }, {
+                label: "取消",
+                action: function (dialog) {
+                    dialog.close();
+                }
+            }]
+        });
+    });
+}
+
+// 给分组下拉绑定事件
+function bindProjectGroupSelect($projectGroupSelect) {
+    // 绑定选择事件
+    $projectGroupSelect.on('change, changed.bs.select', function () {
+        $.loadingT(true);
+        doLoader("/index?projectGroupId=" + $(this).val());
+    });
+}
+
 // 添加连接
 function bindAddConnector() {
     // 绑定添加连接按钮点击事件
@@ -50,7 +115,7 @@ function bindConnectorDropdownMenu() {
         BootstrapDialog.show({
             title: "警告",
             type: BootstrapDialog.TYPE_DANGER,
-            message: "确认删除?",
+            message: "确认删除连接?",
             size: BootstrapDialog.SIZE_NORMAL,
             buttons: [{
                 label: "确定",
@@ -115,6 +180,13 @@ function doPost(url) {
 }
 
 $(function () {
+    // 初始化select插件
+    initSelectIndex($(".select-control"), 1);
+    bindAddProjectGroup();
+    var $projectGroupSelect = $("#projectGroup");
+    bindEditProjectGroup($projectGroupSelect);
+    bindRemoveProjectGroup($projectGroupSelect);
+    bindProjectGroupSelect($projectGroupSelect);
 
     bindAddConnector();
     bindEditConnector();

+ 1 - 3
dbsyncer-web/src/main/resources/static/plugins/js/loading-plus/loading-plus.js

@@ -1,4 +1,4 @@
-/***************************************************** 
+/*****************************************************
  *                                                 *#*
  * @项目:loadingT                                   *#*
  * @作者:AE86                                       *#*
@@ -15,7 +15,6 @@ $.loadingT = {
 		var content = '<i class="fa fa-spin fa-3x fa-refresh"></i>';
 		var $loadingT = $(".loadingT");
 		$loadingT.html("<div class='loading-indicator' unselectable='on' onselectstart='return false;'>"+title+content+"</div>");
-		$loadingT.css({ "height":$(document.body).height()+'px' });
 		var $indicato = $(".loading-indicator");
 		$indicato.css({ "margin-top":($(window).height() / 2) -($indicato.height() / 2) });
 	}
@@ -28,7 +27,6 @@ jQuery.extend({
 		if(a){ $(".loadingT").fadeIn(300);}else{$(".loadingT").fadeOut(300);}
 	},
 	resetIndicato:function(){
-		$(".loadingT").css({ "height":$(document.body).height()+'px' });
 		var $indicato = $(".loading-indicator");
 		$indicato.css({ "margin-top":($(window).height() / 2) -($indicato.height() / 2) });
 	}