This is an automated email from the ASF dual-hosted git repository.

lidongdai pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/incubator-dolphinscheduler.git


The following commit(s) were added to refs/heads/dev by this push:
     new f27c62a  workflow lineage (#2421)
f27c62a is described below

commit f27c62a5dbee2c0f3f51b7975cbc2cd2e792b313
Author: LiemLin <[email protected]>
AuthorDate: Thu Jul 30 13:32:29 2020 +0800

    workflow lineage (#2421)
    
    * workflow lineage
    
    * workflow lineage unit test
    
    * stash
    
    * add apache license
    
    * Update .env
    
    * code optimize
    
    * add unit test
    
    * add apache license
    
    * add apache license
    
    * optimized code
    
    * add postgresql support and optimized code
    
    * Update pom.xml
    
    * Update en_US.js
    
    * Update zh_CN.js
    
    * remove mock code
    
    * inport i18n
    
    * Update pom.xml
    
    * Delete .env
    
    * add env file
    
    Co-authored-by: linhaiqiang <[email protected]>
    Co-authored-by: dailidong <[email protected]>
    Co-authored-by: becarchal <[email protected]>
---
 .../api/controller/WorkFlowLineageController.java  |  81 ++++++++++
 .../apache/dolphinscheduler/api/enums/Status.java  |  14 +-
 .../api/service/WorkFlowLineageService.java        |  97 ++++++++++++
 .../controller/WorkFlowLineageControllerTest.java  |  69 +++++++++
 .../api/service/WorkFlowLineageServiceTest.java    |  88 +++++++++++
 .../dao/datasource/SpringConnectionFactory.java    |  14 ++
 .../dao/entity/WorkFlowLineage.java                |  94 ++++++++++++
 .../dao/entity/WorkFlowRelation.java               |  43 +++---
 .../dao/mapper/WorkFlowLineageMapper.java          |  37 ++---
 .../dao/mapper/WorkFlowLineageMapper.xml           | 103 +++++++++++++
 .../dao/mapper/WorkFlowLineageMapperTest.java      |  62 ++++++++
 dolphinscheduler-ui/build/config.js                |   1 -
 .../src/js/conf/home/pages/projects/index.vue      |   4 +-
 .../projects/pages/kinship/_source/graphGrid.vue   |  63 ++++++++
 .../pages/kinship/_source/graphGridOption.js       | 145 ++++++++++++++++++
 .../projects/pages/kinship/_source/img/dag_bg.png  | Bin 0 -> 5101 bytes
 .../home/pages/projects/pages/kinship/index.vue    | 169 +++++++++++++++++++++
 .../src/js/conf/home/router/index.js               |   8 +
 .../src/js/conf/home/store/index.js                |   2 +
 .../src/js/conf/home/store/kinship/actions.js      |  89 +++++++++++
 .../home/store/{index.js => kinship/getters.js}    |  24 +--
 .../src/js/conf/home/store/{ => kinship}/index.js  |  35 ++---
 .../home/store/{index.js => kinship/mutations.js}  |  22 +--
 .../conf/home/store/{index.js => kinship/state.js} |  28 +---
 .../components/secondaryMenu/_source/menu.js       |  11 +-
 .../src/js/module/i18n/locale/en_US.js             |  11 +-
 .../src/js/module/i18n/locale/zh_CN.js             |   7 +
 pom.xml                                            |   3 +
 .../dolphinscheduler-ui/.gitignore                 |   6 +
 29 files changed, 1192 insertions(+), 138 deletions(-)

diff --git 
a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/WorkFlowLineageController.java
 
b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/WorkFlowLineageController.java
new file mode 100644
index 0000000..895d2cd
--- /dev/null
+++ 
b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/WorkFlowLineageController.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dolphinscheduler.api.controller;
+
+import org.apache.dolphinscheduler.api.service.WorkFlowLineageService;
+import org.apache.dolphinscheduler.api.utils.Result;
+import org.apache.dolphinscheduler.common.utils.ParameterUtils;
+import io.swagger.annotations.ApiParam;
+import org.apache.dolphinscheduler.dao.entity.WorkFlowLineage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static 
org.apache.dolphinscheduler.api.enums.Status.QUERY_WORKFLOW_LINEAGE_ERROR;
+
+@RestController
+@RequestMapping("lineages/{projectId}")
+public class WorkFlowLineageController extends BaseController {
+    private static final Logger logger = 
LoggerFactory.getLogger(WorkFlowLineageController.class);
+
+    @Autowired
+    private WorkFlowLineageService workFlowLineageService;
+
+    @GetMapping(value="/list-name")
+    @ResponseStatus(HttpStatus.OK)
+    public Result<List<WorkFlowLineage>> queryWorkFlowLineageByName(@ApiIgnore 
@RequestParam(value = "searchVal", required = false) String searchVal, 
@ApiParam(name = "projectId", value = "PROJECT_ID", required = true) 
@PathVariable int projectId) {
+        try {
+            searchVal = ParameterUtils.handleEscapes(searchVal);
+            Map<String, Object> result = 
workFlowLineageService.queryWorkFlowLineageByName(searchVal,projectId);
+            return returnDataList(result);
+        } catch (Exception e){
+            logger.error(QUERY_WORKFLOW_LINEAGE_ERROR.getMsg(),e);
+            return error(QUERY_WORKFLOW_LINEAGE_ERROR.getCode(), 
QUERY_WORKFLOW_LINEAGE_ERROR.getMsg());
+        }
+    }
+
+    @GetMapping(value="/list-ids")
+    @ResponseStatus(HttpStatus.OK)
+    public Result<Map<String, Object>> queryWorkFlowLineageByIds(@ApiIgnore 
@RequestParam(value = "ids", required = false) String ids,@ApiParam(name = 
"projectId", value = "PROJECT_ID", required = true) @PathVariable int 
projectId) {
+
+        try {
+            ids = ParameterUtils.handleEscapes(ids);
+            Set<Integer> idsSet = new HashSet<>();
+            if(ids != null) {
+                String[] idsStr = ids.split(",");
+                for (String id : idsStr)
+                {
+                    idsSet.add(Integer.parseInt(id));
+                }
+            }
+
+            Map<String, Object> result = 
workFlowLineageService.queryWorkFlowLineageByIds(idsSet, projectId);
+            return returnDataList(result);
+        } catch (Exception e){
+            logger.error(QUERY_WORKFLOW_LINEAGE_ERROR.getMsg(),e);
+            return error(QUERY_WORKFLOW_LINEAGE_ERROR.getCode(), 
QUERY_WORKFLOW_LINEAGE_ERROR.getMsg());
+        }
+    }
+}
diff --git 
a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/enums/Status.java
 
b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/enums/Status.java
index ec0b8f0..caa390e 100644
--- 
a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/enums/Status.java
+++ 
b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/enums/Status.java
@@ -168,15 +168,15 @@ public enum Status {
     PREVIEW_SCHEDULE_ERROR(10139,"preview schedule error", "预览调度配置错误"),
     PARSE_TO_CRON_EXPRESSION_ERROR(10140,"parse cron to cron expression 
error", "解析调度表达式错误"),
     SCHEDULE_START_TIME_END_TIME_SAME(10141,"The start time must not be the 
same as the end", "开始时间不能和结束时间一样"),
-    DELETE_TENANT_BY_ID_FAIL(10142,"delete tenant by id fail, for there are 
{0} process instances in executing using it", "删除租户失败,有[{0}]个运行中的工作流实例正在使用"),
-    DELETE_TENANT_BY_ID_FAIL_DEFINES(10143,"delete tenant by id fail, for 
there are {0} process definitions using it", "删除租户失败,有[{0}]个工作流定义正在使用"),
-    DELETE_TENANT_BY_ID_FAIL_USERS(10144,"delete tenant by id fail, for there 
are {0} users using it", "删除租户失败,有[{0}]个用户正在使用"),
-    DELETE_WORKER_GROUP_BY_ID_FAIL(10145,"delete worker group by id fail, for 
there are {0} process instances in executing using it", 
"删除Worker分组失败,有[{0}]个运行中的工作流实例正在使用"),
-    QUERY_WORKER_GROUP_FAIL(10146,"query worker group fail ", "查询worker分组失败"),
-    DELETE_WORKER_GROUP_FAIL(10147,"delete worker group fail ", 
"删除worker分组失败"),
+    DELETE_TENANT_BY_ID_FAIL(100142,"delete tenant by id fail, for there are 
{0} process instances in executing using it", "删除租户失败,有[{0}]个运行中的工作流实例正在使用"),
+    DELETE_TENANT_BY_ID_FAIL_DEFINES(100143,"delete tenant by id fail, for 
there are {0} process definitions using it", "删除租户失败,有[{0}]个工作流定义正在使用"),
+    DELETE_TENANT_BY_ID_FAIL_USERS(100144,"delete tenant by id fail, for there 
are {0} users using it", "删除租户失败,有[{0}]个用户正在使用"),
+    DELETE_WORKER_GROUP_BY_ID_FAIL(100145,"delete worker group by id fail, for 
there are {0} process instances in executing using it", 
"删除Worker分组失败,有[{0}]个运行中的工作流实例正在使用"),
+    QUERY_WORKER_GROUP_FAIL(100146,"query worker group fail ", "查询worker分组失败"),
+    DELETE_WORKER_GROUP_FAIL(100147,"delete worker group fail ", 
"删除worker分组失败"),
+    QUERY_WORKFLOW_LINEAGE_ERROR(10143,"query workflow lineage error", 
"查询血缘失败"),
     COPY_PROCESS_DEFINITION_ERROR(10148,"copy process definition error", 
"复制工作流错误"),
     USER_DISABLED(10149,"The current user is disabled", "当前用户已停用"),
-
     UDF_FUNCTION_NOT_EXIST(20001, "UDF function not found", "UDF函数不存在"),
     UDF_FUNCTION_EXISTS(20002, "UDF function already exists", "UDF函数已存在"),
     RESOURCE_NOT_EXIST(20004, "resource not exist", "资源不存在"),
diff --git 
a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/WorkFlowLineageService.java
 
b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/WorkFlowLineageService.java
new file mode 100644
index 0000000..29fd99c
--- /dev/null
+++ 
b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/WorkFlowLineageService.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dolphinscheduler.api.service;
+
+import org.apache.dolphinscheduler.api.enums.Status;
+import org.apache.dolphinscheduler.common.Constants;
+import org.apache.dolphinscheduler.dao.mapper.WorkFlowLineageMapper;
+import org.apache.dolphinscheduler.dao.entity.WorkFlowLineage;
+import org.apache.dolphinscheduler.dao.entity.WorkFlowRelation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+@Service
+public class WorkFlowLineageService extends BaseService {
+
+    @Autowired
+    private WorkFlowLineageMapper workFlowLineageMapper;
+
+    public Map<String, Object> queryWorkFlowLineageByName(String workFlowName, 
int projectId) {
+        Map<String, Object> result = new HashMap<>(5);
+        List<WorkFlowLineage> workFlowLineageList = 
workFlowLineageMapper.queryByName(workFlowName, projectId);
+        result.put(Constants.DATA_LIST, workFlowLineageList);
+        putMsg(result, Status.SUCCESS);
+        return result;
+    }
+
+    private List<WorkFlowRelation> getWorkFlowRelationRecursion(Set<Integer> 
ids, List<WorkFlowRelation> workFlowRelations,Set<Integer> sourceIds) {
+        for(int id : ids) {
+            sourceIds.addAll(ids);
+            List<WorkFlowRelation> workFlowRelationsTmp = 
workFlowLineageMapper.querySourceTarget(id);
+            if(workFlowRelationsTmp != null && 
!workFlowRelationsTmp.isEmpty()) {
+                Set<Integer> idsTmp = new HashSet<>();
+                for(WorkFlowRelation workFlowRelation:workFlowRelationsTmp) {
+                    
if(!sourceIds.contains(workFlowRelation.getTargetWorkFlowId())){
+                        idsTmp.add(workFlowRelation.getTargetWorkFlowId());
+                    }
+                }
+                workFlowRelations.addAll(workFlowRelationsTmp);
+                getWorkFlowRelationRecursion(idsTmp, 
workFlowRelations,sourceIds);
+            }
+        }
+        return workFlowRelations;
+    }
+
+    public Map<String, Object> queryWorkFlowLineageByIds(Set<Integer> ids,int 
projectId) {
+        Map<String, Object> result = new HashMap<>(5);
+        List<WorkFlowLineage> workFlowLineageList = 
workFlowLineageMapper.queryByIds(ids, projectId);
+        Map<String, Object> workFlowLists = new HashMap<>(5);
+        Set<Integer> idsV = new HashSet<>();
+        if(ids == null || ids.isEmpty()){
+            for(WorkFlowLineage workFlowLineage:workFlowLineageList) {
+                idsV.add(workFlowLineage.getWorkFlowId());
+            }
+        } else {
+            idsV = ids;
+        }
+        List<WorkFlowRelation> workFlowRelations = new ArrayList<>();
+        Set<Integer> sourceIds = new HashSet<>();
+        getWorkFlowRelationRecursion(idsV, workFlowRelations, sourceIds);
+
+        Set<Integer> idSet = new HashSet<>();
+        //If the incoming parameter is not empty, you need to add downstream 
workflow detail attributes
+        if(ids != null && !ids.isEmpty()) {
+            for(WorkFlowRelation workFlowRelation : workFlowRelations) {
+                idSet.add(workFlowRelation.getTargetWorkFlowId());
+            }
+            for(int id : ids){
+                idSet.remove(id);
+            }
+            if(!idSet.isEmpty()) {
+                
workFlowLineageList.addAll(workFlowLineageMapper.queryByIds(idSet, projectId));
+            }
+        }
+
+        workFlowLists.put("workFlowList",workFlowLineageList);
+        workFlowLists.put("workFlowRelationList",workFlowRelations);
+        result.put(Constants.DATA_LIST, workFlowLists);
+        putMsg(result, Status.SUCCESS);
+        return result;
+    }
+}
diff --git 
a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/WorkFlowLineageControllerTest.java
 
b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/WorkFlowLineageControllerTest.java
new file mode 100644
index 0000000..7dea15d
--- /dev/null
+++ 
b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/controller/WorkFlowLineageControllerTest.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dolphinscheduler.api.controller;
+
+import org.apache.dolphinscheduler.api.enums.Status;
+import org.apache.dolphinscheduler.api.utils.Result;
+import org.apache.dolphinscheduler.common.utils.JSONUtils;
+import org.junit.Assert;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+
+import static 
org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static 
org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static 
org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+public class WorkFlowLineageControllerTest extends AbstractControllerTest {
+    private static Logger logger = 
LoggerFactory.getLogger(WorkFlowLineageControllerTest.class);
+
+    @Test
+    public void testQueryWorkFlowLineageByName() throws Exception {
+        MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
+        paramsMap.add("searchVal","test");
+        MvcResult mvcResult = mockMvc.perform(get("/lineages/1/list-name")
+                .header("sessionId", sessionId)
+                .params(paramsMap))
+                .andExpect(status().isOk())
+                
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
+                .andReturn();
+        Result result = 
JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), 
Result.class);
+        
Assert.assertEquals(Status.SUCCESS.getCode(),result.getCode().intValue());
+        logger.info(mvcResult.getResponse().getContentAsString());
+
+    }
+
+    @Test
+    public  void testQueryWorkFlowLineageByIds() throws Exception {
+        MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
+        paramsMap.add("ids","1");
+        MvcResult mvcResult = mockMvc.perform(get("/lineages/1/list-ids")
+                .header("sessionId", sessionId)
+                .params(paramsMap))
+                .andExpect(status().isOk())
+                
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
+                .andReturn();
+        Result result = 
JSONUtils.parseObject(mvcResult.getResponse().getContentAsString(), 
Result.class);
+        
Assert.assertEquals(Status.SUCCESS.getCode(),result.getCode().intValue());
+        logger.info(mvcResult.getResponse().getContentAsString());
+    }
+
+}
diff --git 
a/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/WorkFlowLineageServiceTest.java
 
b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/WorkFlowLineageServiceTest.java
new file mode 100644
index 0000000..999e079
--- /dev/null
+++ 
b/dolphinscheduler-api/src/test/java/org/apache/dolphinscheduler/api/service/WorkFlowLineageServiceTest.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dolphinscheduler.api.service;
+
+import org.apache.dolphinscheduler.common.Constants;
+import org.apache.dolphinscheduler.common.utils.EncryptionUtils;
+import org.apache.dolphinscheduler.dao.entity.WorkFlowLineage;
+import org.apache.dolphinscheduler.dao.entity.WorkFlowRelation;
+import org.apache.dolphinscheduler.dao.mapper.WorkFlowLineageMapper;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.*;
+
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class WorkFlowLineageServiceTest {
+
+    @InjectMocks
+    private WorkFlowLineageService workFlowLineageService;
+
+    @Mock
+    private WorkFlowLineageMapper workFlowLineageMapper;
+
+    @Test
+    public void testQueryWorkFlowLineageByName() {
+        String searchVal = "test";
+        when(workFlowLineageMapper.queryByName(searchVal, 
1)).thenReturn(getWorkFlowLineages());
+        Map<String, Object> result = 
workFlowLineageService.queryWorkFlowLineageByName(searchVal,1);
+        List<WorkFlowLineage> workFlowLineageList = 
(List<WorkFlowLineage>)result.get(Constants.DATA_LIST);
+        Assert.assertTrue(workFlowLineageList.size()>0);
+    }
+
+    @Test
+    public void testQueryWorkFlowLineageByIds() {
+
+        Set<Integer> ids = new HashSet<>();
+        ids.add(1);
+        ids.add(2);
+
+        when(workFlowLineageMapper.queryByIds(ids, 
1)).thenReturn(getWorkFlowLineages());
+        
when(workFlowLineageMapper.querySourceTarget(1)).thenReturn(getWorkFlowRelation());
+        Map<String, Object> result = 
workFlowLineageService.queryWorkFlowLineageByIds(ids,1);
+        Map<String, Object> workFlowLists = (Map<String, 
Object>)result.get(Constants.DATA_LIST);
+        List<WorkFlowLineage> workFlowLineages = 
(List<WorkFlowLineage>)workFlowLists.get("workFlowList");
+        List<WorkFlowRelation> workFlowRelations = 
(List<WorkFlowRelation>)workFlowLists.get("workFlowRelationList");
+        Assert.assertTrue(workFlowLineages.size()>0);
+        Assert.assertTrue(workFlowRelations.size()>0);
+    }
+
+    private List<WorkFlowLineage> getWorkFlowLineages() {
+        List<WorkFlowLineage> workFlowLineages = new ArrayList<>();
+        WorkFlowLineage workFlowLineage = new WorkFlowLineage();
+        workFlowLineage.setWorkFlowId(1);
+        workFlowLineage.setWorkFlowName("testdag");
+        workFlowLineages.add(workFlowLineage);
+        return workFlowLineages;
+    }
+
+    private List<WorkFlowRelation> getWorkFlowRelation(){
+        List<WorkFlowRelation> workFlowRelations = new ArrayList<>();
+        WorkFlowRelation workFlowRelation = new WorkFlowRelation();
+        workFlowRelation.setSourceWorkFlowId(1);
+        workFlowRelation.setTargetWorkFlowId(2);
+        workFlowRelations.add(workFlowRelation);
+        return workFlowRelations;
+    }
+
+}
diff --git 
a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/datasource/SpringConnectionFactory.java
 
b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/datasource/SpringConnectionFactory.java
index 9e27d94..5dcdc75 100644
--- 
a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/datasource/SpringConnectionFactory.java
+++ 
b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/datasource/SpringConnectionFactory.java
@@ -26,6 +26,8 @@ import 
org.apache.commons.configuration.ConfigurationException;
 import org.apache.commons.configuration.PropertiesConfiguration;
 import org.apache.dolphinscheduler.common.Constants;
 import org.apache.dolphinscheduler.dao.utils.PropertyUtils;
+import org.apache.ibatis.mapping.DatabaseIdProvider;
+import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
 import org.apache.ibatis.session.SqlSession;
 import org.apache.ibatis.session.SqlSessionFactory;
 import org.apache.ibatis.type.JdbcType;
@@ -39,6 +41,8 @@ import 
org.springframework.core.io.support.PathMatchingResourcePatternResolver;
 import org.springframework.core.io.support.ResourcePatternResolver;
 import org.springframework.jdbc.datasource.DataSourceTransactionManager;
 
+import java.util.Properties;
+
 
 /**
  * data source connection factory
@@ -129,6 +133,7 @@ public class SpringConnectionFactory {
         ResourcePatternResolver resolver = new 
PathMatchingResourcePatternResolver();
         
sqlSessionFactoryBean.setMapperLocations(resolver.getResources("org/apache/dolphinscheduler/dao/mapper/*Mapper.xml"));
         
sqlSessionFactoryBean.setTypeEnumsPackage("org.apache.dolphinscheduler.*.enums");
+        sqlSessionFactoryBean.setDatabaseIdProvider(databaseIdProvider());
         return sqlSessionFactoryBean.getObject();
     }
 
@@ -142,4 +147,13 @@ public class SpringConnectionFactory {
         return new SqlSessionTemplate(sqlSessionFactory());
     }
 
+    @Bean
+    public DatabaseIdProvider databaseIdProvider(){
+        DatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
+        Properties properties = new Properties();
+        properties.setProperty("MySQL", "mysql");
+        properties.setProperty("PostgreSQL", "pg");
+        databaseIdProvider.setProperties(properties);
+        return databaseIdProvider;
+    }
 }
diff --git 
a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/WorkFlowLineage.java
 
b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/WorkFlowLineage.java
new file mode 100644
index 0000000..6c2d9c3
--- /dev/null
+++ 
b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/WorkFlowLineage.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dolphinscheduler.dao.entity;
+
+import java.util.Date;
+
+public class WorkFlowLineage {
+    private int workFlowId;
+    private String workFlowName;
+    private String workFlowPublishStatus;
+    private Date scheduleStartTime;
+    private Date scheduleEndTime;
+    private String crontab;
+    private int schedulePublishStatus;
+    private String sourceWorkFlowId;
+
+    public String getSourceWorkFlowId() {
+        return sourceWorkFlowId;
+    }
+
+    public void setSourceWorkFlowId(String sourceWorkFlowId) {
+        this.sourceWorkFlowId = sourceWorkFlowId;
+    }
+
+    public int getWorkFlowId() {
+        return workFlowId;
+    }
+
+    public void setWorkFlowId(int workFlowId) {
+        this.workFlowId = workFlowId;
+    }
+
+    public String getWorkFlowName() {
+        return workFlowName;
+    }
+
+    public void setWorkFlowName(String workFlowName) {
+        this.workFlowName = workFlowName;
+    }
+
+    public String getWorkFlowPublishStatus() {
+        return workFlowPublishStatus;
+    }
+
+    public void setWorkFlowPublishStatus(String workFlowPublishStatus) {
+        this.workFlowPublishStatus = workFlowPublishStatus;
+    }
+
+    public Date getScheduleStartTime() {
+        return scheduleStartTime;
+    }
+
+    public void setScheduleStartTime(Date scheduleStartTime) {
+        this.scheduleStartTime = scheduleStartTime;
+    }
+
+    public Date getScheduleEndTime() {
+        return scheduleEndTime;
+    }
+
+    public void setScheduleEndTime(Date scheduleEndTime) {
+        this.scheduleEndTime = scheduleEndTime;
+    }
+
+    public String getCrontab() {
+        return crontab;
+    }
+
+    public void setCrontab(String crontab) {
+        this.crontab = crontab;
+    }
+
+    public int getSchedulePublishStatus() {
+        return schedulePublishStatus;
+    }
+
+    public void setSchedulePublishStatus(int schedulePublishStatus) {
+        this.schedulePublishStatus = schedulePublishStatus;
+    }
+}
diff --git a/dolphinscheduler-ui/src/js/conf/home/store/index.js 
b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/WorkFlowRelation.java
similarity index 59%
copy from dolphinscheduler-ui/src/js/conf/home/store/index.js
copy to 
dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/WorkFlowRelation.java
index 5ca2187..c03c68e 100644
--- a/dolphinscheduler-ui/src/js/conf/home/store/index.js
+++ 
b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/WorkFlowRelation.java
@@ -14,24 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import Vue from 'vue'
-import Vuex from 'vuex'
-import dag from './dag'
-import projects from './projects'
-import resource from './resource'
-import security from './security'
-import datasource from './datasource'
-import user from './user'
-import monitor from './monitor'
-Vue.use(Vuex)
-export default new Vuex.Store({
-  modules: {
-    dag,
-    projects,
-    resource,
-    security,
-    datasource,
-    user,
-    monitor
-  }
-})
+package org.apache.dolphinscheduler.dao.entity;
+
+public class WorkFlowRelation {
+    private int sourceWorkFlowId;
+    private int targetWorkFlowId;
+
+    public int getSourceWorkFlowId() {
+        return sourceWorkFlowId;
+    }
+
+    public void setSourceWorkFlowId(int sourceWorkFlowId) {
+        this.sourceWorkFlowId = sourceWorkFlowId;
+    }
+
+    public int getTargetWorkFlowId() {
+        return targetWorkFlowId;
+    }
+
+    public void setTargetWorkFlowId(int targetWorkFlowId) {
+        this.targetWorkFlowId = targetWorkFlowId;
+    }
+}
diff --git a/dolphinscheduler-ui/src/js/conf/home/store/index.js 
b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/WorkFlowLineageMapper.java
similarity index 56%
copy from dolphinscheduler-ui/src/js/conf/home/store/index.js
copy to 
dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/WorkFlowLineageMapper.java
index 5ca2187..3abba3b 100644
--- a/dolphinscheduler-ui/src/js/conf/home/store/index.js
+++ 
b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/mapper/WorkFlowLineageMapper.java
@@ -14,24 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import Vue from 'vue'
-import Vuex from 'vuex'
-import dag from './dag'
-import projects from './projects'
-import resource from './resource'
-import security from './security'
-import datasource from './datasource'
-import user from './user'
-import monitor from './monitor'
-Vue.use(Vuex)
-export default new Vuex.Store({
-  modules: {
-    dag,
-    projects,
-    resource,
-    security,
-    datasource,
-    user,
-    monitor
-  }
-})
+package org.apache.dolphinscheduler.dao.mapper;
+
+import org.apache.dolphinscheduler.dao.entity.WorkFlowLineage;
+import org.apache.dolphinscheduler.dao.entity.WorkFlowRelation;
+import org.apache.ibatis.annotations.Param;
+import java.util.List;
+import java.util.Set;
+
+public interface WorkFlowLineageMapper {
+
+    public List<WorkFlowLineage> queryByName(@Param("searchVal") String 
searchVal, @Param("projectId") int projectId);
+
+    public List<WorkFlowLineage> queryByIds(@Param("ids") Set<Integer> ids, 
@Param("projectId") int projectId);
+
+    public List<WorkFlowRelation> querySourceTarget(@Param("id") int id);
+}
diff --git 
a/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/WorkFlowLineageMapper.xml
 
b/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/WorkFlowLineageMapper.xml
new file mode 100644
index 0000000..823ea0f
--- /dev/null
+++ 
b/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/WorkFlowLineageMapper.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"; >
+<mapper 
namespace="org.apache.dolphinscheduler.dao.mapper.WorkFlowLineageMapper">
+    <select id="queryByName" 
resultType="org.apache.dolphinscheduler.dao.entity.WorkFlowLineage">
+        select tepd.id as work_flow_id,tepd.name as work_flow_name
+        from t_ds_process_definition tepd
+        left join t_ds_schedules tes on tepd.id = tes.process_definition_id
+        where tepd.project_id = #{projectId}
+        <if test="searchVal != null and searchVal != ''">
+            and  tepd.name like concat('%', #{searchVal}, '%')
+        </if>
+    </select>
+    <select id="queryByIds" 
resultType="org.apache.dolphinscheduler.dao.entity.WorkFlowLineage" 
databaseId="mysql">
+        select tepd.id as work_flow_id,tepd.name as work_flow_name,
+               (case when json_extract(tepd.process_definition_json, 
'$**.dependItemList') is not null then 1 else 0 end) as is_depend_work_flow,
+                          json_extract(tepd.process_definition_json, 
'$**.definitionId') as source_work_flow_id,
+                          tepd.release_state as work_flow_publish_status,
+                          tes.start_time as schedule_start_time,
+                          tes.end_time as schedule_end_time,
+                          tes.crontab as crontab,
+                          tes.release_state as schedule_publish_status
+        from t_ds_process_definition tepd
+        left join t_ds_schedules tes on tepd.id = tes.process_definition_id
+        where tepd.project_id = #{projectId}
+        <if test="ids != null and ids.size()>0">
+            and  tepd.id in
+            <foreach collection="ids" index="index" item="i" open="(" 
separator="," close=")">
+                #{i}
+            </foreach>
+        </if>
+    </select>
+
+    <select id="queryByIds" 
resultType="org.apache.dolphinscheduler.dao.entity.WorkFlowLineage" 
databaseId="pg">
+        select a.work_flow_id,
+               a.work_flow_name,
+               a.is_depend_work_flow,
+               array_agg(a.source_id) as source_id,
+               a.work_flow_publish_status,
+               a.schedule_start_time,
+               a.schedule_end_time,
+               a.crontab,
+               a.schedule_publish_status
+         from (
+               select tepd.id as work_flow_id,tepd.name as work_flow_name,
+                      case when 
tepd.process_definition_json::json#>'{tasks,1,dependence}' is not null then 1 
else 0 end as is_depend_work_flow,
+                      
(json_array_elements(tepd.process_definition_json::json#>'{tasks}')#>>'{dependence,dependTaskList,0,dependItemList,0,definitionId}')
 as source_id,
+                      tepd.release_state as work_flow_publish_status,
+                      tes.start_time as schedule_start_time,
+                      tes.end_time as schedule_end_time,
+                      tes.crontab as crontab,
+                      tes.release_state as schedule_publish_status
+                 from t_ds_process_definition tepd
+                 left join t_ds_schedules tes on tepd.id = 
tes.process_definition_id
+                 where tepd.project_id = #{projectId}
+                 <if test="ids != null and ids.size()>0">
+                     and  tepd.id in
+                     <foreach collection="ids" index="index" item="i" open="(" 
separator="," close=")">
+                     #{i}
+                     </foreach>
+                </if>
+             ) a
+        where (a.is_depend_work_flow = 1 and source_id is not null) or 
(a.is_depend_work_flow = 0)
+        group by 
a.work_flow_id,a.work_flow_name,a.is_depend_work_flow,a.work_flow_publish_status,a.schedule_start_time,
+                 a.schedule_end_time,a.crontab,a.schedule_publish_status
+    </select>
+
+    <select id="querySourceTarget" 
resultType="org.apache.dolphinscheduler.dao.entity.WorkFlowRelation" 
databaseId="mysql">
+        select id as target_work_flow_id,#{id} as source_work_flow_id
+        from t_ds_process_definition t
+        where json_extract(t.process_definition_json, '$**.dependItemList') is 
not null
+          and find_in_set(#{id}, 
replace(replace(replace(json_extract(t.process_definition_json, 
'$**.definitionId'), '[', ''),']', ''), ' ', '')) > 0
+    </select>
+
+    <select id="querySourceTarget" 
resultType="org.apache.dolphinscheduler.dao.entity.WorkFlowRelation" 
databaseId="pg">
+        select a.work_flow_id as target_work_flow_id,
+               a.source_id as source_work_flow_id
+         from (
+               select tepd.id as work_flow_id,
+                      
(json_array_elements(tepd.process_definition_json::json#>'{tasks}')#>>'{dependence,dependTaskList,0,dependItemList,0,definitionId}')
 as source_id
+                 from t_ds_process_definition tepd
+                 left join t_ds_schedules tes on tepd.id = 
tes.process_definition_id
+                where tepd.project_id = 1) a
+        where source_id = #{id}::text;
+    </select>
+
+</mapper>
\ No newline at end of file
diff --git 
a/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/mapper/WorkFlowLineageMapperTest.java
 
b/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/mapper/WorkFlowLineageMapperTest.java
new file mode 100644
index 0000000..5645e1c
--- /dev/null
+++ 
b/dolphinscheduler-dao/src/test/java/org/apache/dolphinscheduler/dao/mapper/WorkFlowLineageMapperTest.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.dolphinscheduler.dao.mapper;
+
+import org.apache.dolphinscheduler.dao.entity.WorkFlowLineage;
+import org.apache.dolphinscheduler.dao.entity.WorkFlowRelation;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.Rollback;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+@Transactional
+@Rollback(true)
+public class WorkFlowLineageMapperTest {
+    @Autowired
+    private WorkFlowLineageMapper workFlowLineageMapper;
+
+    @Test
+    public void testQueryByName() {
+        List<WorkFlowLineage> workFlowLineages = 
workFlowLineageMapper.queryByName("test",1);
+        Assert.assertNotEquals(workFlowLineages.size(), 0);
+    }
+
+
+    @Test
+    public void testQueryByIds() {
+        Set<Integer> ids = new HashSet<>();
+        ids.add(1);
+        List<WorkFlowLineage> workFlowLineages = 
workFlowLineageMapper.queryByIds(ids,1);
+        Assert.assertNotEquals(workFlowLineages.size(), 0);
+    }
+
+    @Test
+    public void testQuerySourceTarget() {
+        List<WorkFlowRelation> workFlowRelations = 
workFlowLineageMapper.querySourceTarget(1);
+        Assert.assertNotEquals(workFlowRelations.size(), 0);
+    }
+}
diff --git a/dolphinscheduler-ui/build/config.js 
b/dolphinscheduler-ui/build/config.js
index 53a421d..2800115 100644
--- a/dolphinscheduler-ui/build/config.js
+++ b/dolphinscheduler-ui/build/config.js
@@ -116,7 +116,6 @@ const pages = glob.sync(['*/!(_*).html'], { cwd: viewDir 
}).map(p => {
     minify: minifierConfig
   })
 })
-
 const baseConfig = {
   entry: jsEntry,
   output: {
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/projects/index.vue 
b/dolphinscheduler-ui/src/js/conf/home/pages/projects/index.vue
index 13aa053..cbf4a72 100644
--- a/dolphinscheduler-ui/src/js/conf/home/pages/projects/index.vue
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/projects/index.vue
@@ -17,7 +17,9 @@
 <template>
   <div class="main-layout-box" :class="!isProjectsList ? '' : 'no'">
     <m-secondary-menu :type="'projects'" 
v-if="!isProjectsList"></m-secondary-menu>
-    <router-view></router-view>
+    <transition name="fade">
+      <router-view></router-view>
+    </transition>
   </div>
 </template>
 <script>
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/kinship/_source/graphGrid.vue
 
b/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/kinship/_source/graphGrid.vue
new file mode 100644
index 0000000..eb5ced5
--- /dev/null
+++ 
b/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/kinship/_source/graphGrid.vue
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+<template>
+  <div ref="graph-grid" class="graph-grid"></div>
+</template>
+<script>
+  import echarts from 'echarts'
+  import { mapActions, mapState, mapMutations } from 'vuex'
+  import graphGridOption from './graphGridOption'
+
+  export default {
+    name: 'graphGrid',
+    data () {
+      return {}
+    },
+    props: {
+      id: String,
+      locations: Array,
+      connects: Array,
+      isShowLabel: Boolean
+    },
+    methods: {
+      init () {
+      },
+    },
+    created () {
+    },
+    mounted () {
+      const graphGrid = echarts.init(this.$refs['graph-grid'])
+      graphGrid.setOption(graphGridOption(this.locations, this.connects, 
this.sourceWorkFlowId, this.isShowLabel), true)
+      graphGrid.on('click', (params) => {
+      // Jump to the definition page
+        this.$router.push({ path: 
`/projects/definition/list/${params.data.id}`})
+      });
+    },
+    components: {},
+    computed: {
+      ...mapState('kinship', ['locations', 'connects', 'sourceWorkFlowId'])
+    },
+  }
+</script>
+
+<style lang="scss" rel="stylesheet/scss">
+  .graph-grid {
+    width: 100%;
+    height: calc(100vh - 100px);
+    background: url("./img/dag_bg.png");
+  }
+</style>
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/kinship/_source/graphGridOption.js
 
b/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/kinship/_source/graphGridOption.js
new file mode 100644
index 0000000..117a177
--- /dev/null
+++ 
b/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/kinship/_source/graphGridOption.js
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import _ from 'lodash';
+import i18n from '@/module/i18n/index.js'
+
+const getCategory = (categoryDic, { workFlowPublishStatus, 
schedulePublishStatus, id }, sourceWorkFlowId) => {
+  if (id === sourceWorkFlowId) return categoryDic['active']
+  switch (true) {
+    case workFlowPublishStatus === '0':
+      return categoryDic['0'];
+    case workFlowPublishStatus === '1' && schedulePublishStatus === '0':
+      return categoryDic['10'];
+    case workFlowPublishStatus === '1' && schedulePublishStatus === '1':
+    default:
+      return categoryDic['1'];
+  }
+}
+
+export default function (locations, links, sourceWorkFlowId, isShowLabel) {
+
+  const categoryDic = {
+    'active': { color: '#2D8DF0', category: i18n.$t('KinshipStateActive')},
+    '1': { color: '#00C800', category: i18n.$t('KinshipState1')},
+    '0': { color: '#999999', category: i18n.$t('KinshipState0')},
+    '10': { color: '#FF8F05', category: i18n.$t('KinshipState10')},
+  }
+  const newData = _.map(locations, (item) => {
+    const { color, category } = getCategory(categoryDic, item, 
sourceWorkFlowId)
+    return {
+      ...item,
+      emphasis: {
+        itemStyle: {
+          color
+        },
+      },
+      category
+    }
+  });
+
+  const categories = [
+    { name: categoryDic.active.category},
+    { name: categoryDic['1'].category},
+    { name: categoryDic['0'].category},
+    { name: categoryDic['10'].category},
+  ]
+  let option = {
+    tooltip: {
+      trigger: 'item',
+      triggerOn: 'mousemove',
+      backgroundColor: '#2D303A',
+      padding: [8, 12],
+      formatter: (params) => {
+        if (!params.data.name) return '';
+        const { name, scheduleStartTime, scheduleEndTime, crontab, 
workFlowPublishStatus, schedulePublishStatus } = params.data;
+        const str = `
+      工作流名字:${name}<br/>
+      调度开始时间:${scheduleStartTime}<br/>
+      调度结束时间:${scheduleEndTime}<br/>
+      crontab表达式:${crontab}<br/>
+      工作流发布状态:${workFlowPublishStatus}<br/>
+      调度发布状态:${schedulePublishStatus}<br/>
+      `
+        return str;
+      },
+      color: '#2D303A',
+      textStyle: {
+        rich: {
+          a: {
+            fontSize: 12,
+            color: '#2D303A',
+            lineHeight: 12,
+            align: 'left',
+            padding: [4, 4, 4, 4]
+          },
+        }
+      },
+    },
+    color: [categoryDic.active.color, categoryDic['1'].color, 
categoryDic['0'].color, categoryDic['10'].color],
+    legend: [{
+      orient: 'horizontal',
+      top: 6,
+      left: 6,
+      data: categories,
+    }],
+    series: [{
+      type: 'graph',
+      layout: 'force',
+      nodeScaleRatio: 1.2,
+      draggable: true,
+      animation: false,
+      data: newData,
+      roam: true,
+      symbol: 'roundRect',
+      symbolSize: 70,
+      categories,
+      label: {
+        show: isShowLabel,
+        position: 'inside',
+        formatter: (params) => {
+          if (!params.data.name) return '';
+          const str = params.data.name.split('_').map(item => 
`{a|${item}\n}`).join('')
+          return str;
+        },
+        color: '#222222',
+        textStyle: {
+          rich: {
+            a: {
+              fontSize: 12,
+              color: '#222222',
+              lineHeight: 12,
+              align: 'left',
+              padding: [4, 4, 4, 4]
+            },
+          }
+        }
+      },
+      edgeSymbol: ['circle', 'arrow'],
+      edgeSymbolSize: [4, 12],
+      force: {
+        repulsion: 1000,
+        edgeLength: 300
+      },
+      links: links,
+      lineStyle: {
+        color: '#999999'
+      }
+    }]
+  };
+
+  return option
+}
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/kinship/_source/img/dag_bg.png
 
b/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/kinship/_source/img/dag_bg.png
new file mode 100644
index 0000000..d9fa098
Binary files /dev/null and 
b/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/kinship/_source/img/dag_bg.png
 differ
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/kinship/index.vue 
b/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/kinship/index.vue
new file mode 100644
index 0000000..2472a69
--- /dev/null
+++ 
b/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/kinship/index.vue
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+<template>
+  <div class="home-main index-model">
+    <div class="project-kinship-content">
+      <div class="search-bar">
+        <x-select filterable clearable
+                  :placeholder="$t('Process Name')"
+                  @on-change="onChange"
+                  :style="inputFocusStyle"
+                  v-tooltip="tooltipOption(currentItemName)"
+                  size="small">
+          <x-option
+            v-for="work in workList"
+            :key="work.id"
+            :value="work.id"
+            :label="work.name"
+            v-tooltip="tooltipOption(work.name)"
+            >
+          </x-option>
+        </x-select>
+        <x-button type="primary"
+                  icon="ans-icon-dot-circle"
+                  size="small"
+                  v-tooltip.small.top.start="$t('Reset')"
+                  @click="reset"
+                  ></x-button>
+        <x-button type="ghost"
+                  icon="ans-icon-eye"
+                  size="small"
+                  v-tooltip.small.top="$t('Dag label display control')"
+                  @click="changeLabel"
+                  ></x-button>
+      </div>
+      <graph-grid v-if="!isLoading && !!locations.length" 
:isShowLabel="isShowLabel"></graph-grid>
+      <template v-if="!isLoading && !locations.length">
+        <m-no-data style="height: 100%;"></m-no-data>
+      </template>
+    </div>
+    <m-spin :is-spin="isLoading" :fullscreen="false"></m-spin>
+  </div>
+</template>
+<script>
+  import _ from 'lodash'
+  import { mapActions, mapState } from 'vuex'
+  import mSpin from '@/module/components/spin/spin'
+  import mNoData from '@/module/components/noData/noData'
+  import listUrlParamHandle from '@/module/mixin/listUrlParamHandle'
+  import graphGrid from './_source/graphGrid.vue'
+
+
+
+  export default {
+    name: 'projects-kinship-index',
+    components: { graphGrid, mSpin, mNoData },
+    data () {
+      return {
+        isLoading: true,
+        isShowLabel: true,
+        currentItemName: '',
+      }
+    },
+    props: {},
+    methods: {
+      ...mapActions('kinship', ['getWorkFlowList','getWorkFlowDAG']),
+      /**
+       * init
+       */
+      init () {
+        this.isLoading = true
+        // Promise Get node needs data
+        Promise.all([
+          // get process definition
+          this.getWorkFlowList(),
+          this.getWorkFlowDAG(),
+        ]).then((data) => {
+          this.isLoading = false
+        }).catch(() => {
+          this.isLoading = false
+        })
+      },
+      /**
+       * reset
+       */
+      reset() {
+        this.isLoading = true;
+        this.$nextTick(() => {
+          this.isLoading = false;
+        })
+      },
+      async onChange(item) {
+        const { value, label } = item || {};
+        this.isLoading = true;
+        this.currentItemName = label;
+        try {
+          await this.getWorkFlowDAG(value);
+        } catch (error) {
+          this.$message.error(error.msg || '')
+        }
+        this.isLoading = false;
+      },
+      tooltipOption(text) {
+        return {
+          text,
+          maxWidth: '500px',
+          placement: 'top',
+          theme: 'dark',
+          triggerEvent: 'mouseenter',
+          large: false,
+        }
+      },
+      changeLabel() {
+        this.isLoading = true;
+        this.isShowLabel = !this.isShowLabel;
+        this.$nextTick(() => {
+          this.isLoading = false;
+        })
+      }
+    },
+    watch: {
+      // router
+      '$route' (a) {
+        // url no params get instance list
+      }
+    },
+    created () {
+      this.init()
+    },
+    computed: {
+      ...mapState('kinship', ['locations', 'workList']),
+      inputFocusStyle() {
+        return `width:280px`
+      },
+    },
+    mounted () {
+    }
+  }
+</script>
+<style lang="scss" rel="stylesheet/scss">
+  .project-kinship-content {
+    position: relative;
+    width: 100%;
+    height: calc(100vh - 100px);
+    background: url("./_source/img/dag_bg.png");
+    .search-bar {
+      position: absolute;
+      right: 8px;
+      top: 10px;
+      z-index: 2;
+      .ans-input {
+        transition: width 300ms ease-in-out;
+      }
+    }
+  }
+</style>
diff --git a/dolphinscheduler-ui/src/js/conf/home/router/index.js 
b/dolphinscheduler-ui/src/js/conf/home/router/index.js
index b4236f3..b65586c 100644
--- a/dolphinscheduler-ui/src/js/conf/home/router/index.js
+++ b/dolphinscheduler-ui/src/js/conf/home/router/index.js
@@ -58,6 +58,14 @@ const router = new Router({
           }
         },
         {
+          path: '/projects/kinship',
+          name: 'projects-kinship',
+          component: resolve => 
require(['../pages/projects/pages/kinship/index'], resolve),
+          meta: {
+            title: `${i18n.$t('Kinship')}`
+          }
+        },
+        {
           path: '/projects/list',
           name: 'projects-list',
           component: resolve => 
require(['../pages/projects/pages/list/index'], resolve),
diff --git a/dolphinscheduler-ui/src/js/conf/home/store/index.js 
b/dolphinscheduler-ui/src/js/conf/home/store/index.js
index 5ca2187..a7e5824 100644
--- a/dolphinscheduler-ui/src/js/conf/home/store/index.js
+++ b/dolphinscheduler-ui/src/js/conf/home/store/index.js
@@ -17,6 +17,7 @@
 import Vue from 'vue'
 import Vuex from 'vuex'
 import dag from './dag'
+import kinship from './kinship'
 import projects from './projects'
 import resource from './resource'
 import security from './security'
@@ -28,6 +29,7 @@ export default new Vuex.Store({
   modules: {
     dag,
     projects,
+    kinship,
     resource,
     security,
     datasource,
diff --git a/dolphinscheduler-ui/src/js/conf/home/store/kinship/actions.js 
b/dolphinscheduler-ui/src/js/conf/home/store/kinship/actions.js
new file mode 100644
index 0000000..f87a57d
--- /dev/null
+++ b/dolphinscheduler-ui/src/js/conf/home/store/kinship/actions.js
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import _ from 'lodash'
+import io from '@/module/io'
+import localStore from '@/module/util/localStorage'
+
+export default {
+  /**
+   * Get workFlow DAG
+   */
+  getWorkFlowList ({ state }, payload) {
+    const projectId = localStore.getItem('projectId');
+    return new Promise((resolve, reject) => {
+      const url = `lineages/${projectId}/list-name`;
+      io.get(url, {
+        searchVal: payload,
+      }, res => {
+        const workList = [];
+        if (res.data) {
+          _.map(res.data, (item) => {
+            workList.push({
+              id: `${item.workFlowId}`,
+              name: item.workFlowName,
+            })
+          })
+        }
+        state.workList = workList /* JSON.parse(connects) */
+        resolve(res.data)
+      }).catch(res => {
+        reject(res)
+      })
+    })
+  },
+  /**
+   * Get workFlow DAG
+   */
+  getWorkFlowDAG ({ state }, payload) {
+    const projectId = localStore.getItem('projectId');
+    return new Promise((resolve, reject) => {
+      const url = `lineages/${projectId}/list-ids`;
+      io.get(url, {
+        ids: payload,
+      }, res => {
+        let locations = [];
+        let connects = [];
+        if (res.data.workFlowList) {
+          locations = _.uniqBy(res.data.workFlowList, 'workFlowId').map((item) 
=> ({
+            id: `${item.workFlowId}`,
+            name: item.workFlowName,
+            workFlowPublishStatus: item.workFlowPublishStatus,
+            scheduleStartTime: item.scheduleStartTime,
+            scheduleEndTime: item.scheduleEndTime,
+            crontab: item.crontab,
+            schedulePublishStatus: item.schedulePublishStatus
+          }))
+        }
+        if (res.data.workFlowRelationList) {
+          connects = _.map(res.data.workFlowRelationList, (item) => ({
+            source: `${item.sourceWorkFlowId}`, // should be string, or 
connects will not show by echarts
+            target: `${item.targetWorkFlowId}`, // should be string, or 
connects will not show by echarts
+          }))
+        }
+        state.sourceWorkFlowId = payload || '';
+        // locations
+        state.locations = locations /* JSON.parse(locations) */
+        // connects
+        state.connects = connects /* JSON.parse(connects) */
+        resolve(res.data)
+      }).catch(res => {
+        reject(res)
+      })
+    })
+  },
+}
diff --git a/dolphinscheduler-ui/src/js/conf/home/store/index.js 
b/dolphinscheduler-ui/src/js/conf/home/store/kinship/getters.js
similarity index 65%
copy from dolphinscheduler-ui/src/js/conf/home/store/index.js
copy to dolphinscheduler-ui/src/js/conf/home/store/kinship/getters.js
index 5ca2187..e84c864 100644
--- a/dolphinscheduler-ui/src/js/conf/home/store/index.js
+++ b/dolphinscheduler-ui/src/js/conf/home/store/kinship/getters.js
@@ -14,24 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import Vue from 'vue'
-import Vuex from 'vuex'
-import dag from './dag'
-import projects from './projects'
-import resource from './resource'
-import security from './security'
-import datasource from './datasource'
-import user from './user'
-import monitor from './monitor'
-Vue.use(Vuex)
-export default new Vuex.Store({
-  modules: {
-    dag,
-    projects,
-    resource,
-    security,
-    datasource,
-    user,
-    monitor
-  }
-})
+
+export default {
+}
diff --git a/dolphinscheduler-ui/src/js/conf/home/store/index.js 
b/dolphinscheduler-ui/src/js/conf/home/store/kinship/index.js
similarity index 65%
copy from dolphinscheduler-ui/src/js/conf/home/store/index.js
copy to dolphinscheduler-ui/src/js/conf/home/store/kinship/index.js
index 5ca2187..24abd1c 100644
--- a/dolphinscheduler-ui/src/js/conf/home/store/index.js
+++ b/dolphinscheduler-ui/src/js/conf/home/store/kinship/index.js
@@ -14,24 +14,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import Vue from 'vue'
-import Vuex from 'vuex'
-import dag from './dag'
-import projects from './projects'
-import resource from './resource'
-import security from './security'
-import datasource from './datasource'
-import user from './user'
-import monitor from './monitor'
-Vue.use(Vuex)
-export default new Vuex.Store({
-  modules: {
-    dag,
-    projects,
-    resource,
-    security,
-    datasource,
-    user,
-    monitor
-  }
-})
+
+import actions from './actions'
+import getters from './getters'
+import mutations from './mutations'
+import state from './state'
+
+export default {
+  strict: true,
+  namespaced: true,
+  state,
+  getters,
+  mutations,
+  actions
+}
diff --git a/dolphinscheduler-ui/src/js/conf/home/store/index.js 
b/dolphinscheduler-ui/src/js/conf/home/store/kinship/mutations.js
similarity index 65%
copy from dolphinscheduler-ui/src/js/conf/home/store/index.js
copy to dolphinscheduler-ui/src/js/conf/home/store/kinship/mutations.js
index 5ca2187..2d71cd8 100644
--- a/dolphinscheduler-ui/src/js/conf/home/store/index.js
+++ b/dolphinscheduler-ui/src/js/conf/home/store/kinship/mutations.js
@@ -14,24 +14,4 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import Vue from 'vue'
-import Vuex from 'vuex'
-import dag from './dag'
-import projects from './projects'
-import resource from './resource'
-import security from './security'
-import datasource from './datasource'
-import user from './user'
-import monitor from './monitor'
-Vue.use(Vuex)
-export default new Vuex.Store({
-  modules: {
-    dag,
-    projects,
-    resource,
-    security,
-    datasource,
-    user,
-    monitor
-  }
-})
+export default {}
diff --git a/dolphinscheduler-ui/src/js/conf/home/store/index.js 
b/dolphinscheduler-ui/src/js/conf/home/store/kinship/state.js
similarity index 65%
copy from dolphinscheduler-ui/src/js/conf/home/store/index.js
copy to dolphinscheduler-ui/src/js/conf/home/store/kinship/state.js
index 5ca2187..6f22b86 100644
--- a/dolphinscheduler-ui/src/js/conf/home/store/index.js
+++ b/dolphinscheduler-ui/src/js/conf/home/store/kinship/state.js
@@ -14,24 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import Vue from 'vue'
-import Vuex from 'vuex'
-import dag from './dag'
-import projects from './projects'
-import resource from './resource'
-import security from './security'
-import datasource from './datasource'
-import user from './user'
-import monitor from './monitor'
-Vue.use(Vuex)
-export default new Vuex.Store({
-  modules: {
-    dag,
-    projects,
-    resource,
-    security,
-    datasource,
-    user,
-    monitor
-  }
-})
+
+export default {
+  sourceWorkFlowId: '',
+  workList: [],
+  locations: [],
+  connects: []
+}
diff --git 
a/dolphinscheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js 
b/dolphinscheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js
index 6487c2f..56834a2 100644
--- a/dolphinscheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js
+++ b/dolphinscheduler-ui/src/js/module/components/secondaryMenu/_source/menu.js
@@ -31,8 +31,17 @@ const menu = {
       children: []
     },
     {
-      name: `${i18n.$t('Process')}`,
+      name: `${i18n.$t('Kinship')}`,
       id: 1,
+      path: 'projects-kinship',
+      isOpen: true,
+      disabled: true,
+      icon: 'ans-icon-node',
+      children: []
+    },
+    {
+      name: `${i18n.$t('Process')}`,
+      id: 2,
       path: '',
       isOpen: true,
       disabled: true,
diff --git a/dolphinscheduler-ui/src/js/module/i18n/locale/en_US.js 
b/dolphinscheduler-ui/src/js/module/i18n/locale/en_US.js
index eaca45d..8afa0ed 100755
--- a/dolphinscheduler-ui/src/js/module/i18n/locale/en_US.js
+++ b/dolphinscheduler-ui/src/js/module/i18n/locale/en_US.js
@@ -545,8 +545,8 @@ export default {
   '0 means unlimited by count': '0 means unlimited',
   'Modify User': 'Modify User',
   'Whether directory': 'Whether directory',
-  Yes: 'Yes',
-  No: 'No',
+  'Yes': 'Yes',
+  'No': 'No',
   'Hadoop Custom Params': 'Hadoop Params',
   'Sqoop Advanced Parameters': 'Sqoop Params',
   'Sqoop Job Name': 'Job Name',
@@ -605,6 +605,13 @@ export default {
   'Successful branch flow and failed branch flow are required': 'conditions 
node Successful and failed branch flow are required',
   'Unauthorized or deleted resources': 'Unauthorized or deleted resources',
   'Please delete all non-existent resources': 'Please delete all non-existent 
resources',
+  'Kinship': 'Workflow relationship',
+  'Reset': 'Reset',
+  'KinshipStateActive': 'Active',
+  'KinshipState1': 'Online',
+  'KinshipState0': 'Workflow is not online',
+  'KinshipState10': 'Scheduling is not online',
+  'Dag label display control': 'Dag label display control',
   'Enable': 'Enable',
   'Timeout Settings': 'Timeout Settings',
   'Connect Timeout':'Connect Timeout',
diff --git a/dolphinscheduler-ui/src/js/module/i18n/locale/zh_CN.js 
b/dolphinscheduler-ui/src/js/module/i18n/locale/zh_CN.js
index 8d6ecd7..cda0d4a 100755
--- a/dolphinscheduler-ui/src/js/module/i18n/locale/zh_CN.js
+++ b/dolphinscheduler-ui/src/js/module/i18n/locale/zh_CN.js
@@ -605,6 +605,13 @@ export default {
   'Successful branch flow and failed branch flow are required': 
'conditions节点成功和失败分支流转必填',
   'Unauthorized or deleted resources': '未授权或已删除资源',
   'Please delete all non-existent resources': '请删除所有未授权或已删除资源',
+  'Kinship': '工作流关系',
+  'Reset': '重置',
+  'KinshipStateActive': '当前选择',
+  'KinshipState1': '已上线',
+  'KinshipState0': '工作流未上线',
+  'KinshipState10': '调度未上线',
+  'Dag label display control': 'Dag节点名称显隐',
   'Enable': '启用',
   'Disable': '停用',
   'The Worker group no longer exists, please select the correct Worker 
group!': '该Worker分组已经不存在,请选择正确的Worker分组!',
diff --git a/pom.xml b/pom.xml
index 196e95a..b469f38 100644
--- a/pom.xml
+++ b/pom.xml
@@ -552,6 +552,7 @@
 
         
<finalName>apache-dolphinscheduler-incubating-${project.version}</finalName>
 
+
         <pluginManagement>
             <plugins>
 
@@ -733,7 +734,9 @@
                         
<include>**/api/service/UserAlertGroupServiceTest.java</include>
                         <include>**/api/service/UsersServiceTest.java</include>
                         
<include>**/api/service/WorkerGroupServiceTest.java</include>
+                        
<include>**/api/service/WorkFlowLineageServiceTest.java</include>
                         
<include>**/api/controller/ProcessDefinitionControllerTest.java</include>
+                        
<include>**/api/controller/WorkFlowLineageControllerTest.java</include>
                         
<include>**/api/utils/exportprocess/DataSourceParamTest.java</include>
                         
<include>**/api/utils/exportprocess/DependentParamTest.java</include>
                         <include>**/api/utils/CheckUtilsTest.java</include>
diff --git a/repository/dolphinscheduler/dolphinscheduler-ui/.gitignore 
b/repository/dolphinscheduler/dolphinscheduler-ui/.gitignore
new file mode 100644
index 0000000..27c1333
--- /dev/null
+++ b/repository/dolphinscheduler/dolphinscheduler-ui/.gitignore
@@ -0,0 +1,6 @@
+.idea
+.settings
+package-lock.json
+.classpath
+.project
+node_modules
\ No newline at end of file

Reply via email to