You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by li...@apache.org on 2023/03/20 03:09:27 UTC

[incubator-devlake] branch main updated: Issues/4567 zentao bp (#4705)

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

likyh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new 807a5ce99 Issues/4567 zentao bp (#4705)
807a5ce99 is described below

commit 807a5ce99734c13ced99c932cfb36b1dc8aeef2a
Author: Likyh <ya...@meri.co>
AuthorDate: Mon Mar 20 11:09:23 2023 +0800

    Issues/4567 zentao bp (#4705)
    
    * fix: update scope helper
    
    * feat: add bp support for zentao
    
    * feat: support zentao bp in config-ui
    
    * feat: update zentao option
    
    * fix: fix task bug
    
    * fix: update e2e test
    
    * fix: for linter
    
    * fix: for review
---
 .../helpers/pluginhelper/api/remote_api_helper.go  |  30 +++-
 backend/helpers/pluginhelper/api/scope_helper.go   |  14 +-
 backend/plugins/bamboo/api/init.go                 |   4 +-
 backend/plugins/bamboo/models/project.go           |  14 +-
 backend/plugins/zentao/api/blueprint.go            |  67 --------
 backend/plugins/zentao/api/blueprint_V200_test.go  | 175 +++++++++++++++++++++
 backend/plugins/zentao/api/blueprint_v200.go       | 140 +++++++++++++++++
 backend/plugins/zentao/api/init.go                 |  32 ++++
 backend/plugins/zentao/api/remote.go               | 138 ++++++++++++++++
 backend/plugins/zentao/api/scope.go                | 131 +++++++++++++++
 backend/plugins/zentao/e2e/account_test.go         |   1 -
 backend/plugins/zentao/e2e/bug_test.go             |   1 -
 backend/plugins/zentao/e2e/department_test.go      |   1 -
 backend/plugins/zentao/e2e/execution_test.go       |  12 +-
 backend/plugins/zentao/e2e/product_test.go         |  12 +-
 .../e2e/raw_tables/_raw_zentao_api_accounts.csv    |  24 +--
 .../zentao/e2e/raw_tables/_raw_zentao_api_bugs.csv |  12 +-
 .../e2e/raw_tables/_raw_zentao_api_departments.csv |  24 +--
 .../e2e/raw_tables/_raw_zentao_api_executions.csv  |   6 +-
 .../raw_tables/_raw_zentao_api_executions_real.csv |   2 -
 .../e2e/raw_tables/_raw_zentao_api_products.csv    |   4 +-
 .../e2e/raw_tables/_raw_zentao_api_stories.csv     |  18 +--
 .../e2e/raw_tables/_raw_zentao_api_tasks.csv       |  10 +-
 .../snapshot_tables/_tool_zentao_executions.csv    |   3 +-
 .../e2e/snapshot_tables/_tool_zentao_tasks.csv     |   6 +-
 .../e2e/snapshot_tables/boards_execution.csv       |   2 -
 .../e2e/snapshot_tables/execution_board_sprint.csv |   3 +
 .../e2e/snapshot_tables/execution_sprint.csv       |   3 +
 backend/plugins/zentao/e2e/story_test.go           |   1 -
 backend/plugins/zentao/e2e/task_test.go            |   1 -
 backend/plugins/zentao/impl/impl.go                |  27 +++-
 backend/plugins/zentao/models/product.go           | 116 ++++++++++----
 backend/plugins/zentao/models/project.go           |  84 +++++-----
 backend/plugins/zentao/tasks/account_collector.go  |   2 +-
 backend/plugins/zentao/tasks/account_convertor.go  |   1 -
 backend/plugins/zentao/tasks/account_extractor.go  |   1 -
 backend/plugins/zentao/tasks/bug_collector.go      |   5 +-
 backend/plugins/zentao/tasks/bug_convertor.go      |   1 -
 backend/plugins/zentao/tasks/bug_extractor.go      |   1 -
 .../plugins/zentao/tasks/department_collector.go   |   2 +-
 .../plugins/zentao/tasks/department_convertor.go   |   1 -
 .../plugins/zentao/tasks/department_extractor.go   |   1 -
 .../plugins/zentao/tasks/execution_collector.go    |  17 +-
 .../plugins/zentao/tasks/execution_convertor.go    |  42 +++--
 .../plugins/zentao/tasks/execution_extractor.go    |   2 +-
 backend/plugins/zentao/tasks/product_collector.go  |  76 ---------
 backend/plugins/zentao/tasks/product_convertor.go  |   6 +-
 backend/plugins/zentao/tasks/product_extractor.go  | 101 ------------
 backend/plugins/zentao/tasks/project_collector.go  |  78 ---------
 .../{product_convertor.go => project_convertor.go} |  38 ++---
 backend/plugins/zentao/tasks/project_extractor.go  |  69 --------
 backend/plugins/zentao/tasks/story_collector.go    |   5 +-
 backend/plugins/zentao/tasks/story_convertor.go    |   3 +-
 backend/plugins/zentao/tasks/story_extractor.go    |   1 -
 backend/plugins/zentao/tasks/task_collector.go     |  29 +++-
 backend/plugins/zentao/tasks/task_convertor.go     |   6 +-
 backend/plugins/zentao/tasks/task_data.go          |  24 ++-
 backend/plugins/zentao/tasks/task_extractor.go     |   1 -
 backend/plugins/zentao/zentao.go                   |   2 -
 .../blueprint/create/step-three/use-columns.tsx    |   2 +-
 .../pages/blueprint/detail/panel/configuration.tsx |   2 +-
 .../src/pages/pipeline/components/task/index.tsx   |   7 +
 config-ui/src/plugins/components/data-scope/api.ts |   6 +
 .../src/plugins/components/data-scope/index.tsx    |   5 +
 .../components/data-scope/use-data-scope.ts        |  31 +++-
 config-ui/src/plugins/register/zentao/config.ts    |   1 -
 .../src/plugins/register/zentao/data-scope.tsx     |  54 +++++++
 config-ui/src/plugins/register/zentao/index.ts     |   1 +
 .../plugins/register/zentao/{index.ts => types.ts} |   8 +-
 69 files changed, 1103 insertions(+), 647 deletions(-)

diff --git a/backend/helpers/pluginhelper/api/remote_api_helper.go b/backend/helpers/pluginhelper/api/remote_api_helper.go
index 93be9e04a..64840fe4b 100644
--- a/backend/helpers/pluginhelper/api/remote_api_helper.go
+++ b/backend/helpers/pluginhelper/api/remote_api_helper.go
@@ -74,6 +74,30 @@ func NewRemoteHelper[Conn plugin.ApiConnection, Scope plugin.ToolLayerScope, Api
 	}
 }
 
+type NoRemoteGroupResponse struct {
+}
+
+func (NoRemoteGroupResponse) GroupId() string {
+	return ""
+}
+
+func (NoRemoteGroupResponse) GroupName() string {
+	return ""
+}
+
+type BaseRemoteGroupResponse struct {
+	Id   string
+	Name string
+}
+
+func (g BaseRemoteGroupResponse) GroupId() string {
+	return g.Id
+}
+
+func (g BaseRemoteGroupResponse) GroupName() string {
+	return g.Name
+}
+
 const remoteScopesPerPage int = 100
 const TypeProject string = "scope"
 const TypeGroup string = "group"
@@ -115,7 +139,9 @@ func (r *RemoteApiHelper[Conn, Scope, ApiScope, Group]) GetScopesFromRemote(inpu
 	// list groups part
 	if queryData.Tag == TypeGroup {
 		var resBody []Group
-		resBody, err = getGroup(r.basicRes, gid, queryData, connection)
+		if getGroup != nil {
+			resBody, err = getGroup(r.basicRes, gid, queryData, connection)
+		}
 		if err != nil {
 			return nil, err
 		}
@@ -144,7 +170,7 @@ func (r *RemoteApiHelper[Conn, Scope, ApiScope, Group]) GetScopesFromRemote(inpu
 	}
 
 	// list projects part
-	if queryData.Tag == TypeProject {
+	if queryData.Tag == TypeProject && getScope != nil {
 		var resBody []ApiScope
 		resBody, err = getScope(r.basicRes, gid, queryData, connection)
 		if err != nil {
diff --git a/backend/helpers/pluginhelper/api/scope_helper.go b/backend/helpers/pluginhelper/api/scope_helper.go
index 749372831..e0eaee3e6 100644
--- a/backend/helpers/pluginhelper/api/scope_helper.go
+++ b/backend/helpers/pluginhelper/api/scope_helper.go
@@ -83,7 +83,7 @@ func (c *ScopeApiHelper[Conn, Scope, Tr]) Put(input *plugin.ApiResourceInput) (*
 	}
 	err := errors.Convert(DecodeMapStruct(input.Body, &req))
 	if err != nil {
-		return nil, errors.BadInput.Wrap(err, "decoding Github repo error")
+		return nil, errors.BadInput.Wrap(err, "decoding scope error")
 	}
 	// Extract the connection ID from the input.Params map
 	connectionId, _ := extractFromReqParam(input.Params)
@@ -117,9 +117,11 @@ func (c *ScopeApiHelper[Conn, Scope, Tr]) Put(input *plugin.ApiResourceInput) (*
 			return nil, err
 		}
 	}
-	err = c.save(&req.Data)
-	if err != nil {
-		return nil, err
+	if req.Data != nil && len(req.Data) > 0 {
+		err = c.save(&req.Data)
+		if err != nil {
+			return nil, err
+		}
 	}
 
 	// Save the scopes to the database
@@ -284,13 +286,13 @@ func setScopeFields(p interface{}, connectionId uint64, createdDate *time.Time,
 
 	// set CreatedDate
 	createdDateField := pValue.FieldByName("CreatedDate")
-	if createdDateField.IsValid() {
+	if createdDateField.IsValid() && createdDateField.Type().AssignableTo(reflect.TypeOf(createdDate)) {
 		createdDateField.Set(reflect.ValueOf(createdDate))
 	}
 
 	// set UpdatedDate
 	updatedDateField := pValue.FieldByName("UpdatedDate")
-	if !updatedDateField.IsValid() {
+	if !updatedDateField.IsValid() || (updatedDate != nil && !updatedDateField.Type().AssignableTo(reflect.TypeOf(updatedDate))) {
 		return
 	}
 	if updatedDate == nil {
diff --git a/backend/plugins/bamboo/api/init.go b/backend/plugins/bamboo/api/init.go
index 7705fd797..e70961d82 100644
--- a/backend/plugins/bamboo/api/init.go
+++ b/backend/plugins/bamboo/api/init.go
@@ -27,7 +27,7 @@ import (
 var vld *validator.Validate
 var connectionHelper *api.ConnectionApiHelper
 var scopeHelper *api.ScopeApiHelper[models.BambooConnection, models.BambooProject, models.BambooTransformationRule]
-var remoteHelper *api.RemoteApiHelper[models.BambooConnection, models.BambooProject, models.ApiBambooProject, models.GroupResponse]
+var remoteHelper *api.RemoteApiHelper[models.BambooConnection, models.BambooProject, models.ApiBambooProject, api.NoRemoteGroupResponse]
 
 var basicRes context.BasicRes
 
@@ -43,7 +43,7 @@ func Init(br context.BasicRes) {
 		vld,
 		connectionHelper,
 	)
-	remoteHelper = api.NewRemoteHelper[models.BambooConnection, models.BambooProject, models.ApiBambooProject, models.GroupResponse](
+	remoteHelper = api.NewRemoteHelper[models.BambooConnection, models.BambooProject, models.ApiBambooProject, api.NoRemoteGroupResponse](
 		basicRes,
 		vld,
 		connectionHelper,
diff --git a/backend/plugins/bamboo/models/project.go b/backend/plugins/bamboo/models/project.go
index 2232604e6..56fe9879d 100644
--- a/backend/plugins/bamboo/models/project.go
+++ b/backend/plugins/bamboo/models/project.go
@@ -21,10 +21,11 @@ import (
 	"encoding/json"
 	"github.com/apache/incubator-devlake/core/models/common"
 	"github.com/apache/incubator-devlake/core/plugin"
+	"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
 )
 
 var _ plugin.ToolLayerScope = (*BambooProject)(nil)
-var _ plugin.ApiGroup = (*GroupResponse)(nil)
+var _ plugin.ApiGroup = (*api.NoRemoteGroupResponse)(nil)
 var _ plugin.ApiScope = (*ApiBambooProject)(nil)
 
 type BambooProject struct {
@@ -103,14 +104,3 @@ func (apiProject ApiBambooProject) ConvertApiScope() plugin.ToolLayerScope {
 	b.Href = apiProject.Link.Href
 	return b
 }
-
-type GroupResponse struct {
-}
-
-func (p GroupResponse) GroupId() string {
-	return ""
-}
-
-func (p GroupResponse) GroupName() string {
-	return ""
-}
diff --git a/backend/plugins/zentao/api/blueprint.go b/backend/plugins/zentao/api/blueprint.go
deleted file mode 100644
index fc0625597..000000000
--- a/backend/plugins/zentao/api/blueprint.go
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
-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 api
-
-import (
-	"encoding/json"
-	"github.com/apache/incubator-devlake/core/errors"
-	"github.com/apache/incubator-devlake/core/plugin"
-	helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
-	"github.com/apache/incubator-devlake/plugins/zentao/tasks"
-)
-
-func MakePipelinePlan(subtaskMetas []plugin.SubTaskMeta, connectionId uint64, scope []*plugin.BlueprintScopeV100) (plugin.PipelinePlan, errors.Error) {
-	var err error
-	plan := make(plugin.PipelinePlan, len(scope))
-	for i, scopeElem := range scope {
-		taskOptions := make(map[string]interface{})
-		err = json.Unmarshal(scopeElem.Options, &taskOptions)
-		if err != nil {
-			return nil, errors.Default.WrapRaw(err)
-		}
-		taskOptions["connectionId"] = connectionId
-
-		/*
-		   var transformationRules tasks.JiraTransformationRule
-		   if len(scopeElem.Transformation) > 0 {
-		       err = json.Unmarshal(scopeElem.Transformation, &transformationRules)
-		       if err != nil {
-		           return nil, err
-		       }
-		   }
-		*/
-		//taskOptions["transformationRules"] = transformationRules
-		_, err := tasks.DecodeAndValidateTaskOptions(taskOptions)
-		if err != nil {
-			return nil, errors.Default.WrapRaw(err)
-		}
-		// subtasks
-		subtasks, err := helper.MakePipelinePlanSubtasks(subtaskMetas, scopeElem.Entities)
-		if err != nil {
-			return nil, errors.Default.WrapRaw(err)
-		}
-		plan[i] = plugin.PipelineStage{
-			{
-				Plugin:   "zentao",
-				Subtasks: subtasks,
-				Options:  taskOptions,
-			},
-		}
-	}
-	return plan, nil
-}
diff --git a/backend/plugins/zentao/api/blueprint_V200_test.go b/backend/plugins/zentao/api/blueprint_V200_test.go
new file mode 100644
index 000000000..cb96293e5
--- /dev/null
+++ b/backend/plugins/zentao/api/blueprint_V200_test.go
@@ -0,0 +1,175 @@
+/*
+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 api
+
+import (
+	"testing"
+
+	"github.com/apache/incubator-devlake/core/models/common"
+	"github.com/apache/incubator-devlake/core/models/domainlayer"
+	"github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+	"github.com/apache/incubator-devlake/core/plugin"
+	helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+	mockcontext "github.com/apache/incubator-devlake/mocks/core/context"
+	mockdal "github.com/apache/incubator-devlake/mocks/core/dal"
+	mockplugin "github.com/apache/incubator-devlake/mocks/core/plugin"
+	"github.com/apache/incubator-devlake/plugins/zentao/models"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+)
+
+func TestMakeDataSourcePipelinePlanV200(t *testing.T) {
+	connection := &models.ZentaoConnection{
+		BaseConnection: helper.BaseConnection{
+			Name: "zentao-test",
+			Model: common.Model{
+				ID: 1,
+			},
+		},
+		ZentaoConn: models.ZentaoConn{
+			RestConnection: helper.RestConnection{
+				Endpoint:         "https://zentao.example.org/api.php/v1/",
+				Proxy:            "",
+				RateLimitPerHour: 0,
+			},
+			BasicAuth: helper.BasicAuth{
+				Username: "Username",
+				Password: "Password",
+			},
+		},
+	}
+	mockMeta := mockplugin.NewPluginMeta(t)
+	mockMeta.On("RootPkgPath").Return("github.com/apache/incubator-devlake/plugins/zentao")
+	err := plugin.RegisterPlugin("zentao", mockMeta)
+	assert.Nil(t, err)
+	// Refresh Global Variables and set the sql mock
+	basicRes = NewMockBasicRes()
+	bs := &plugin.BlueprintScopeV200{
+		Entities: []string{"TICKET"},
+		Id:       "project/1",
+	}
+	bs2 := &plugin.BlueprintScopeV200{
+		Entities: []string{"TICKET"},
+		Id:       "product/1",
+	}
+	bpScopes := make([]*plugin.BlueprintScopeV200, 0)
+	bpScopes = append(bpScopes, bs, bs2)
+	syncPolicy := &plugin.BlueprintSyncPolicy{}
+
+	plan := make(plugin.PipelinePlan, len(bpScopes))
+	plan, scopes, err := makePipelinePlanV200(nil, plan, bpScopes, connection, syncPolicy)
+	assert.Nil(t, err)
+	basicRes = NewMockBasicRes()
+
+	expectPlan := plugin.PipelinePlan{
+		plugin.PipelineStage{
+			{
+				Plugin:   "zentao",
+				Subtasks: []string{},
+				Options: map[string]interface{}{
+					"ConnectionId": uint64(1),
+					"productId":    int64(0),
+					"projectId":    int64(1),
+				},
+			},
+		},
+		plugin.PipelineStage{
+			{
+				Plugin:   "zentao",
+				Subtasks: []string{},
+				Options: map[string]interface{}{
+					"ConnectionId": uint64(1),
+					"productId":    int64(1),
+					"projectId":    int64(0),
+				},
+			},
+		},
+	}
+	assert.Equal(t, expectPlan, plan)
+	expectScopes := make([]plugin.Scope, 0)
+	scopeTicket1 := &ticket.Board{
+		DomainEntity: domainlayer.DomainEntity{
+			Id: "zentao:ZentaoProject:1:1",
+		},
+		Name:        "test/testRepo",
+		Description: "",
+		Url:         "",
+		CreatedDate: nil,
+		Type:        `project`,
+	}
+	scopeTicket2 := &ticket.Board{
+		DomainEntity: domainlayer.DomainEntity{
+			Id: "zentao:ZentaoProduct:1:1",
+		},
+		Name:        "test/testRepo",
+		Description: "",
+		Url:         "",
+		CreatedDate: nil,
+		Type:        `product/normal`,
+	}
+
+	expectScopes = append(expectScopes, scopeTicket1, scopeTicket2)
+	assert.Equal(t, expectScopes, scopes)
+}
+
+// NewMockBasicRes FIXME ...
+func NewMockBasicRes() *mockcontext.BasicRes {
+	testZentaoProduct := &models.ZentaoProduct{
+		ConnectionId: 1,
+		Id:           1,
+		Name:         "test/testRepo",
+		Type:         `product/normal`,
+		//TransformationRuleId: 1,
+	}
+	testZentaoProject := &models.ZentaoProject{
+		ConnectionId: 1,
+		Id:           1,
+		Name:         "test/testRepo",
+		Type:         `project`,
+		//TransformationRuleId: 1,
+	}
+
+	//testTransformationRule := &models.ZentaoTransformation{
+	//	Model: common.Model{
+	//		ID: 1,
+	//	},
+	//	Name:            "Zentao transformation rule",
+	//}
+	mockRes := new(mockcontext.BasicRes)
+	mockDal := new(mockdal.Dal)
+
+	mockDal.On("First", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+		dst := args.Get(0).(*models.ZentaoProject)
+		*dst = *testZentaoProject
+	}).Return(nil).Once()
+
+	mockDal.On("First", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+		dst := args.Get(0).(*models.ZentaoProduct)
+		*dst = *testZentaoProduct
+	}).Return(nil).Once()
+
+	//mockDal.On("First", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
+	//	dst := args.Get(0).(*models.ZentaoTransformation)
+	//	*dst = *testTransformationRule
+	//}).Return(nil).Once()
+
+	mockRes.On("GetDal").Return(mockDal)
+	mockRes.On("GetConfig", mock.Anything).Return("")
+
+	return mockRes
+}
diff --git a/backend/plugins/zentao/api/blueprint_v200.go b/backend/plugins/zentao/api/blueprint_v200.go
new file mode 100644
index 000000000..6d0805247
--- /dev/null
+++ b/backend/plugins/zentao/api/blueprint_v200.go
@@ -0,0 +1,140 @@
+/*
+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 api
+
+import (
+	"fmt"
+	"github.com/apache/incubator-devlake/core/dal"
+	"github.com/apache/incubator-devlake/core/errors"
+	"github.com/apache/incubator-devlake/core/models/domainlayer"
+	"github.com/apache/incubator-devlake/core/models/domainlayer/didgen"
+	"github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+	"github.com/apache/incubator-devlake/core/plugin"
+	"github.com/apache/incubator-devlake/core/utils"
+	helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+	"github.com/apache/incubator-devlake/plugins/zentao/models"
+	"github.com/apache/incubator-devlake/plugins/zentao/tasks"
+	"github.com/go-playground/validator/v10"
+	"strings"
+	"time"
+)
+
+func MakeDataSourcePipelinePlanV200(subtaskMetas []plugin.SubTaskMeta, connectionId uint64, bpScopes []*plugin.BlueprintScopeV200, syncPolicy *plugin.BlueprintSyncPolicy) (plugin.PipelinePlan, []plugin.Scope, errors.Error) {
+	connectionHelper := helper.NewConnectionHelper(basicRes, validator.New())
+	// get the connection info for url
+	connection := &models.ZentaoConnection{}
+	err := connectionHelper.FirstById(connection, connectionId)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	plan := make(plugin.PipelinePlan, len(bpScopes))
+	plan, scopes, err := makePipelinePlanV200(subtaskMetas, plan, bpScopes, connection, syncPolicy)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	return plan, scopes, nil
+}
+
+func makePipelinePlanV200(
+	subtaskMetas []plugin.SubTaskMeta,
+	plan plugin.PipelinePlan,
+	bpScopes []*plugin.BlueprintScopeV200,
+	connection *models.ZentaoConnection,
+	syncPolicy *plugin.BlueprintSyncPolicy,
+) (plugin.PipelinePlan, []plugin.Scope, errors.Error) {
+	var err errors.Error
+	domainScopes := make([]plugin.Scope, 0)
+	for i, bpScope := range bpScopes {
+		stage := plan[i]
+		if stage == nil {
+			stage = plugin.PipelineStage{}
+		}
+		// construct task options
+		op := &tasks.ZentaoOptions{
+			ConnectionId: connection.ID,
+		}
+
+		scopeType := strings.Split(bpScope.Id, `/`)[0]
+		scopeId := strings.Split(bpScope.Id, `/`)[1]
+		if scopeType == `project` {
+			scope := &models.ZentaoProject{}
+			// get repo from db
+			err = basicRes.GetDal().First(scope, dal.Where(`connection_id = ? AND id = ?`, connection.ID, scopeId))
+			if err != nil {
+				return nil, nil, errors.Default.Wrap(err, fmt.Sprintf("fail to find zentao project %s", bpScope.Id))
+			}
+			op.ProjectId = scope.Id
+
+			if utils.StringsContains(bpScope.Entities, plugin.DOMAIN_TYPE_TICKET) {
+				scopeTicket := &ticket.Board{
+					DomainEntity: domainlayer.DomainEntity{
+						Id: didgen.NewDomainIdGenerator(&models.ZentaoProject{}).Generate(connection.ID, scope.Id),
+					},
+					Name: scope.Name,
+					Type: scope.Type,
+				}
+				domainScopes = append(domainScopes, scopeTicket)
+			}
+		} else {
+			scope := &models.ZentaoProduct{}
+			// get repo from db
+			err = basicRes.GetDal().First(scope, dal.Where(`connection_id = ? AND id = ?`, connection.ID, scopeId))
+			if err != nil {
+				return nil, nil, errors.Default.Wrap(err, fmt.Sprintf("fail to find zentao product %s", bpScope.Id))
+			}
+			op.ProductId = scope.Id
+
+			if utils.StringsContains(bpScope.Entities, plugin.DOMAIN_TYPE_TICKET) {
+				scopeTicket := &ticket.Board{
+					DomainEntity: domainlayer.DomainEntity{
+						Id: didgen.NewDomainIdGenerator(&models.ZentaoProduct{}).Generate(connection.ID, scope.Id),
+					},
+					Name: scope.Name,
+					Type: scope.Type,
+				}
+				domainScopes = append(domainScopes, scopeTicket)
+			}
+		}
+
+		if syncPolicy.TimeAfter != nil {
+			op.TimeAfter = syncPolicy.TimeAfter.Format(time.RFC3339)
+		}
+		options, err := tasks.EncodeTaskOptions(op)
+		if err != nil {
+			return nil, nil, err
+		}
+
+		subtasks, err := helper.MakePipelinePlanSubtasks(subtaskMetas, bpScope.Entities)
+		if err != nil {
+			return nil, nil, err
+		}
+		stage = append(stage, &plugin.PipelineTask{
+			Plugin:   "zentao",
+			Subtasks: subtasks,
+			Options:  options,
+		})
+		if err != nil {
+			return nil, nil, err
+		}
+
+		plan[i] = stage
+	}
+	return plan, domainScopes, nil
+}
diff --git a/backend/plugins/zentao/api/init.go b/backend/plugins/zentao/api/init.go
index d92c2b334..8108249bc 100644
--- a/backend/plugins/zentao/api/init.go
+++ b/backend/plugins/zentao/api/init.go
@@ -20,11 +20,23 @@ package api
 import (
 	"github.com/apache/incubator-devlake/core/context"
 	"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+	"github.com/apache/incubator-devlake/plugins/zentao/models"
 	"github.com/go-playground/validator/v10"
 )
 
+type MixScopes struct {
+	ZentaoProduct *models.ZentaoProduct `json:"product"`
+	ZentaoProject *models.ZentaoProject `json:"project"`
+}
+type NoTransformation struct{}
+
 var vld *validator.Validate
 var connectionHelper *api.ConnectionApiHelper
+var productScopeHelper *api.ScopeApiHelper[models.ZentaoConnection, models.ZentaoProduct, NoTransformation]
+var projectScopeHelper *api.ScopeApiHelper[models.ZentaoConnection, models.ZentaoProject, NoTransformation]
+
+var productRemoteHelper *api.RemoteApiHelper[models.ZentaoConnection, models.ZentaoProduct, models.ZentaoProductRes, api.BaseRemoteGroupResponse]
+var projectRemoteHelper *api.RemoteApiHelper[models.ZentaoConnection, models.ZentaoProject, models.ZentaoProject, api.NoRemoteGroupResponse]
 var basicRes context.BasicRes
 
 func Init(br context.BasicRes) {
@@ -34,4 +46,24 @@ func Init(br context.BasicRes) {
 		basicRes,
 		vld,
 	)
+	productScopeHelper = api.NewScopeHelper[models.ZentaoConnection, models.ZentaoProduct, NoTransformation](
+		basicRes,
+		vld,
+		connectionHelper,
+	)
+	projectScopeHelper = api.NewScopeHelper[models.ZentaoConnection, models.ZentaoProject, NoTransformation](
+		basicRes,
+		vld,
+		connectionHelper,
+	)
+	productRemoteHelper = api.NewRemoteHelper[models.ZentaoConnection, models.ZentaoProduct, models.ZentaoProductRes, api.BaseRemoteGroupResponse](
+		basicRes,
+		vld,
+		connectionHelper,
+	)
+	projectRemoteHelper = api.NewRemoteHelper[models.ZentaoConnection, models.ZentaoProject, models.ZentaoProject, api.NoRemoteGroupResponse](
+		basicRes,
+		vld,
+		connectionHelper,
+	)
 }
diff --git a/backend/plugins/zentao/api/remote.go b/backend/plugins/zentao/api/remote.go
new file mode 100644
index 000000000..9152f5b18
--- /dev/null
+++ b/backend/plugins/zentao/api/remote.go
@@ -0,0 +1,138 @@
+/*
+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 api
+
+import (
+	"context"
+	"fmt"
+	"net/url"
+
+	context2 "github.com/apache/incubator-devlake/core/context"
+	"github.com/apache/incubator-devlake/core/errors"
+	"github.com/apache/incubator-devlake/core/plugin"
+	"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+	"github.com/apache/incubator-devlake/plugins/zentao/models"
+)
+
+type ProductResponse struct {
+	Limit  int                       `json:"limit"`
+	Page   int                       `json:"page"`
+	Total  int                       `json:"total"`
+	Values []models.ZentaoProductRes `json:"products"`
+}
+
+type ProjectResponse struct {
+	Limit  int                    `json:"limit"`
+	Page   int                    `json:"page"`
+	Total  int                    `json:"total"`
+	Values []models.ZentaoProject `json:"projects"`
+}
+
+func getGroup(basicRes context2.BasicRes, gid string, queryData *plugin.QueryData, connection models.ZentaoConnection) ([]api.BaseRemoteGroupResponse, errors.Error) {
+	return []api.BaseRemoteGroupResponse{
+		{
+			Id:   `products`,
+			Name: `Products`,
+		},
+		{
+			Id:   `projects`,
+			Name: `Projects`,
+		},
+	}, nil
+}
+
+// RemoteScopes list all available scope for users
+// @Summary list all available scope for users
+// @Description list all available scope for users
+// @Tags plugins/zentao
+// @Accept application/json
+// @Param connectionId path int false "connection ID"
+// @Param groupId query string false "group ID"
+// @Param pageToken query string false "page Token"
+// @Success 200  {object} api.RemoteScopesOutput
+// @Failure 400  {object} shared.ApiBody "Bad Request"
+// @Failure 500  {object} shared.ApiBody "Internal Error"
+// @Router /plugins/zentao/connections/{connectionId}/remote-scopes [GET]
+func RemoteScopes(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
+	groupId, ok := input.Query["groupId"]
+	if !ok || len(groupId) == 0 {
+		groupId = []string{""}
+	}
+	gid := groupId[0]
+	if gid == "" {
+		return productRemoteHelper.GetScopesFromRemote(input, getGroup, nil)
+	} else if gid == `products` {
+		return productRemoteHelper.GetScopesFromRemote(input,
+			nil,
+			func(basicRes context2.BasicRes, gid string, queryData *plugin.QueryData, connection models.ZentaoConnection) ([]models.ZentaoProductRes, errors.Error) {
+				query := initialQuery(queryData)
+				// create api client
+				apiClient, err := api.NewApiClientFromConnection(context.TODO(), basicRes, &connection)
+				if err != nil {
+					return nil, err
+				}
+
+				query.Set("sort", "name")
+				// list projects part
+				res, err := apiClient.Get("/products", query, nil)
+				if err != nil {
+					return nil, err
+				}
+
+				resBody := &ProductResponse{}
+				err = api.UnmarshalResponse(res, resBody)
+				if err != nil {
+					return nil, err
+				}
+				return resBody.Values, nil
+			})
+	} else if gid == `projects` {
+		return projectRemoteHelper.GetScopesFromRemote(input,
+			nil,
+			func(basicRes context2.BasicRes, gid string, queryData *plugin.QueryData, connection models.ZentaoConnection) ([]models.ZentaoProject, errors.Error) {
+				query := initialQuery(queryData)
+				// create api client
+				apiClient, err := api.NewApiClientFromConnection(context.TODO(), basicRes, &connection)
+				if err != nil {
+					return nil, err
+				}
+
+				query.Set("sort", "name")
+				// list projects part
+				res, err := apiClient.Get("/projects", query, nil)
+				if err != nil {
+					return nil, err
+				}
+
+				resBody := &ProjectResponse{}
+				err = api.UnmarshalResponse(res, resBody)
+				if err != nil {
+					return nil, err
+				}
+				return resBody.Values, nil
+			})
+	}
+	return nil, nil
+}
+
+func initialQuery(queryData *plugin.QueryData) url.Values {
+	query := url.Values{}
+	query.Set("page", fmt.Sprintf("%v", queryData.Page))
+	query.Set("limit", fmt.Sprintf("%v", queryData.PerPage))
+	return query
+}
diff --git a/backend/plugins/zentao/api/scope.go b/backend/plugins/zentao/api/scope.go
new file mode 100644
index 000000000..974ec8ec0
--- /dev/null
+++ b/backend/plugins/zentao/api/scope.go
@@ -0,0 +1,131 @@
+/*
+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 api
+
+import (
+	"github.com/apache/incubator-devlake/core/errors"
+	"github.com/apache/incubator-devlake/core/plugin"
+	"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+	"github.com/apache/incubator-devlake/plugins/zentao/models"
+)
+
+type ProductScopeRes struct {
+	models.ZentaoProduct
+	TransformationRuleName string `json:"transformationRuleName,omitempty"`
+}
+
+type ProductScopeReq api.ScopeReq[models.ZentaoProduct]
+
+type ProjectScopeRes struct {
+	models.ZentaoProject
+	TransformationRuleName string `json:"transformationRuleName,omitempty"`
+}
+
+type ProjectScopeReq api.ScopeReq[models.ZentaoProject]
+
+// PutProductScope create or update zentao products
+// @Summary create or update zentao products
+// @Description Create or update zentao products
+// @Tags plugins/zentao
+// @Accept application/json
+// @Param connectionId path int true "connection ID"
+// @Param scope body ProductScopeReq true "json"
+// @Success 200  {object} []models.ZentaoProduct
+// @Failure 400  {object} shared.ApiBody "Bad Request"
+// @Failure 500  {object} shared.ApiBody "Internal Error"
+// @Router /plugins/zentao/connections/{connectionId}/product/scopes [PUT]
+func PutProductScope(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
+	return productScopeHelper.Put(input)
+}
+
+// PutProjectScope create or update zentao projects
+// @Summary create or update zentao projects
+// @Description Create or update zentao projects
+// @Tags plugins/zentao
+// @Accept application/json
+// @Param connectionId path int true "connection ID"
+// @Param scope body ProjectScopeReq true "json"
+// @Success 200  {object} []models.ZentaoProject
+// @Failure 400  {object} shared.ApiBody "Bad Request"
+// @Failure 500  {object} shared.ApiBody "Internal Error"
+// @Router /plugins/zentao/connections/{connectionId}/project/scopes [PUT]
+func PutProjectScope(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
+	return projectScopeHelper.Put(input)
+}
+
+// UpdateProductScope patch to zentao product
+// @Summary patch to zentao product
+// @Description patch to zentao product
+// @Tags plugins/zentao
+// @Accept application/json
+// @Param connectionId path int true "connection ID"
+// @Param scopeId path int true "scope ID"
+// @Param scope body models.ZentaoProduct true "json"
+// @Success 200  {object} models.ZentaoProduct
+// @Failure 400  {object} shared.ApiBody "Bad Request"
+// @Failure 500  {object} shared.ApiBody "Internal Error"
+// @Router /plugins/zentao/connections/{connectionId}/scopes/product/{scopeId} [PATCH]
+func UpdateProductScope(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
+	return productScopeHelper.Update(input, "id")
+}
+
+// UpdateProjectScope patch to zentao project
+// @Summary patch to zentao project
+// @Description patch to zentao project
+// @Tags plugins/zentao
+// @Accept application/json
+// @Param connectionId path int true "connection ID"
+// @Param scopeId path int true "scope ID"
+// @Param scope body models.ZentaoProject true "json"
+// @Success 200  {object} models.ZentaoProject
+// @Failure 400  {object} shared.ApiBody "Bad Request"
+// @Failure 500  {object} shared.ApiBody "Internal Error"
+// @Router /plugins/zentao/connections/{connectionId}/scopes/project/{scopeId} [PATCH]
+func UpdateProjectScope(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
+	return projectScopeHelper.Update(input, "id")
+}
+
+// TODO GetScopeList get zentao projects and products
+
+// GetProductScope get one product
+// @Summary get one product
+// @Description get one product
+// @Tags plugins/zentao
+// @Param connectionId path int true "connection ID"
+// @Param scopeId path int true "scope ID"
+// @Success 200  {object} ProductScopeRes
+// @Failure 400  {object} shared.ApiBody "Bad Request"
+// @Failure 500  {object} shared.ApiBody "Internal Error"
+// @Router /plugins/zentao/connections/{connectionId}/scopes/product/{scopeId} [GET]
+func GetProductScope(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
+	return productScopeHelper.GetScope(input, "id")
+}
+
+// GetProjectScope get one project
+// @Summary get one project
+// @Description get one project
+// @Tags plugins/zentao
+// @Param connectionId path int true "connection ID"
+// @Param scopeId path int true "scope ID"
+// @Success 200  {object} ProjectScopeRes
+// @Failure 400  {object} shared.ApiBody "Bad Request"
+// @Failure 500  {object} shared.ApiBody "Internal Error"
+// @Router /plugins/zentao/connections/{connectionId}/scopes/project/{scopeId} [GET]
+func GetProjectScope(input *plugin.ApiResourceInput) (*plugin.ApiResourceOutput, errors.Error) {
+	return projectScopeHelper.GetScope(input, "id")
+}
diff --git a/backend/plugins/zentao/e2e/account_test.go b/backend/plugins/zentao/e2e/account_test.go
index b8046cacf..f92104046 100644
--- a/backend/plugins/zentao/e2e/account_test.go
+++ b/backend/plugins/zentao/e2e/account_test.go
@@ -37,7 +37,6 @@ func TestZentaoAccountDataFlow(t *testing.T) {
 			ConnectionId: 1,
 			ProjectId:    1,
 			ProductId:    3,
-			ExecutionId:  1,
 		},
 	}
 
diff --git a/backend/plugins/zentao/e2e/bug_test.go b/backend/plugins/zentao/e2e/bug_test.go
index 44f07e601..4057d29f5 100644
--- a/backend/plugins/zentao/e2e/bug_test.go
+++ b/backend/plugins/zentao/e2e/bug_test.go
@@ -37,7 +37,6 @@ func TestZentaoBugDataFlow(t *testing.T) {
 			ConnectionId: 1,
 			ProjectId:    1,
 			ProductId:    3,
-			ExecutionId:  1,
 		},
 	}
 
diff --git a/backend/plugins/zentao/e2e/department_test.go b/backend/plugins/zentao/e2e/department_test.go
index 7955d7380..34b138296 100644
--- a/backend/plugins/zentao/e2e/department_test.go
+++ b/backend/plugins/zentao/e2e/department_test.go
@@ -37,7 +37,6 @@ func TestZentaoDepartmentDataFlow(t *testing.T) {
 			ConnectionId: 1,
 			ProjectId:    1,
 			ProductId:    3,
-			ExecutionId:  1,
 		},
 	}
 
diff --git a/backend/plugins/zentao/e2e/execution_test.go b/backend/plugins/zentao/e2e/execution_test.go
index 6434576d4..464e3d1ee 100644
--- a/backend/plugins/zentao/e2e/execution_test.go
+++ b/backend/plugins/zentao/e2e/execution_test.go
@@ -37,7 +37,6 @@ func TestZentaoExecutionDataFlow(t *testing.T) {
 			ConnectionId: 1,
 			ProjectId:    1,
 			ProductId:    3,
-			ExecutionId:  1,
 		},
 	}
 
@@ -53,10 +52,15 @@ func TestZentaoExecutionDataFlow(t *testing.T) {
 		IgnoreTypes: []interface{}{common.NoPKModel{}},
 	})
 
-	dataflowTester.FlushTabler(&ticket.Board{})
+	dataflowTester.FlushTabler(&ticket.Sprint{})
+	dataflowTester.FlushTabler(&ticket.BoardSprint{})
 	dataflowTester.Subtask(tasks.ConvertExecutionMeta, taskData)
-	dataflowTester.VerifyTableWithOptions(&ticket.Board{}, e2ehelper.TableOptions{
-		CSVRelPath:  "./snapshot_tables/boards_execution.csv",
+	dataflowTester.VerifyTableWithOptions(&ticket.Sprint{}, e2ehelper.TableOptions{
+		CSVRelPath:  "./snapshot_tables/execution_sprint.csv",
+		IgnoreTypes: []interface{}{common.NoPKModel{}},
+	})
+	dataflowTester.VerifyTableWithOptions(&ticket.BoardSprint{}, e2ehelper.TableOptions{
+		CSVRelPath:  "./snapshot_tables/execution_board_sprint.csv",
 		IgnoreTypes: []interface{}{common.NoPKModel{}},
 	})
 }
diff --git a/backend/plugins/zentao/e2e/product_test.go b/backend/plugins/zentao/e2e/product_test.go
index 9a7831d31..676c29199 100644
--- a/backend/plugins/zentao/e2e/product_test.go
+++ b/backend/plugins/zentao/e2e/product_test.go
@@ -37,21 +37,11 @@ func TestZentaoProductDataFlow(t *testing.T) {
 			ConnectionId: 1,
 			ProjectId:    1,
 			ProductId:    3,
-			ExecutionId:  9,
 		},
 	}
 
 	// import raw data table
-	dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_zentao_api_products.csv",
-		"_raw_zentao_api_products")
-
-	// verify extraction
-	dataflowTester.FlushTabler(&models.ZentaoProduct{})
-	dataflowTester.Subtask(tasks.ExtractProductMeta, taskData)
-	dataflowTester.VerifyTableWithOptions(&models.ZentaoProduct{}, e2ehelper.TableOptions{
-		CSVRelPath:  "./snapshot_tables/_tool_zentao_products.csv",
-		IgnoreTypes: []interface{}{common.NoPKModel{}},
-	})
+	dataflowTester.ImportCsvIntoTabler("./snapshot_tables/_tool_zentao_products.csv", &models.ZentaoProduct{})
 
 	dataflowTester.FlushTabler(&ticket.Board{})
 	dataflowTester.Subtask(tasks.ConvertProductMeta, taskData)
diff --git a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_accounts.csv b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_accounts.csv
index f1aaf1229..7500e19a1 100644
--- a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_accounts.csv
+++ b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_accounts.csv
@@ -1,13 +1,13 @@
 id,params,data,url,input,created_at
-31,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":10,""dept"":1,""account"":""testManager"",""realname"":""\u6d4b\u8bd5\u7ecf\u7406"",""role"":""qd"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
-32,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":9,""dept"":3,""account"":""tester3"",""realname"":""\u6d4b\u8bd5\u4e19"",""role"":""qa"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
-33,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":8,""dept"":3,""account"":""tester2"",""realname"":""\u6d4b\u8bd5\u4e59"",""role"":""qa"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
-34,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":7,""dept"":3,""account"":""tester1"",""realname"":""\u6d4b\u8bd5\u7532"",""role"":""qa"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
-35,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":6,""dept"":2,""account"":""dev3"",""realname"":""\u5f00\u53d1\u4e19"",""role"":""dev"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
-36,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":5,""dept"":2,""account"":""dev2"",""realname"":""\u5f00\u53d1\u4e59"",""role"":""dev"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
-37,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":4,""dept"":2,""account"":""dev1"",""realname"":""\u5f00\u53d1\u7532"",""role"":""dev"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
-38,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":3,""dept"":6,""account"":""projectManager"",""realname"":""\u9879\u76ee\u7ecf\u7406"",""role"":""pm"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
-39,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":2,""dept"":5,""account"":""productManager"",""realname"":""\u4ea7\u54c1\u7ecf\u7406"",""role"":""po"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
-40,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":1,""dept"":0,""account"":""devlake"",""realname"":""devlake"",""role"":"""",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
-41,"{""ConnectionId"":3,""ProductId"":2,""ExecutionId"":1,""ProjectId"":3}","{""id"":11,""dept"":5,""account"":""productManager"",""realname"":""\u4ea7\u54c1\u7ecf\u7406"",""role"":""po"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
-42,"{""ConnectionId"":2,""ProductId"":1,""ExecutionId"":3,""ProjectId"":3}","{""id"":12,""dept"":0,""account"":""devlake"",""realname"":""devlake"",""role"":"""",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
+31,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":10,""dept"":1,""account"":""testManager"",""realname"":""\u6d4b\u8bd5\u7ecf\u7406"",""role"":""qd"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
+32,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":9,""dept"":3,""account"":""tester3"",""realname"":""\u6d4b\u8bd5\u4e19"",""role"":""qa"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
+33,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":8,""dept"":3,""account"":""tester2"",""realname"":""\u6d4b\u8bd5\u4e59"",""role"":""qa"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
+34,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":7,""dept"":3,""account"":""tester1"",""realname"":""\u6d4b\u8bd5\u7532"",""role"":""qa"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
+35,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":6,""dept"":2,""account"":""dev3"",""realname"":""\u5f00\u53d1\u4e19"",""role"":""dev"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
+36,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":5,""dept"":2,""account"":""dev2"",""realname"":""\u5f00\u53d1\u4e59"",""role"":""dev"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
+37,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":4,""dept"":2,""account"":""dev1"",""realname"":""\u5f00\u53d1\u7532"",""role"":""dev"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
+38,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":3,""dept"":6,""account"":""projectManager"",""realname"":""\u9879\u76ee\u7ecf\u7406"",""role"":""pm"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
+39,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":2,""dept"":5,""account"":""productManager"",""realname"":""\u4ea7\u54c1\u7ecf\u7406"",""role"":""po"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
+40,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":1,""dept"":0,""account"":""devlake"",""realname"":""devlake"",""role"":"""",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
+41,"{""ConnectionId"":3,""ProductId"":2,""ProjectId"":3}","{""id"":11,""dept"":5,""account"":""productManager"",""realname"":""\u4ea7\u54c1\u7ecf\u7406"",""role"":""po"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
+42,"{""ConnectionId"":2,""ProductId"":1,""ProjectId"":3}","{""id"":12,""dept"":0,""account"":""devlake"",""realname"":""devlake"",""role"":"""",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:57.970
diff --git a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_bugs.csv b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_bugs.csv
index d69bb7129..c60979cae 100644
--- a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_bugs.csv
+++ b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_bugs.csv
@@ -1,7 +1,7 @@
 id,params,data,url,input,created_at
-1,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":4,""project"":7,""product"":3,""injection"":0,""identify"":0,""branch"":0,""module"":11,""execution"":1,""plan"":0,""story"":4,""storyVersion"":1,""task"":9,""toTask"":0,""toStory"":0,""title"":""\u552e\u540e\u670d\u52a1\u9875\u9762\u95ee\u9898"",""keywords"":"""",""severity"":3,""pri"":1,""type"":""codeerror"",""os"":"""",""browser"":"""",""hardware"":"""",""found"":"""",""steps"":""\u003Cp\u003E[\u6b65\ [...]
-2,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":3,""project"":7,""product"":3,""injection"":0,""identify"":0,""branch"":0,""module"":10,""execution"":1,""plan"":0,""story"":3,""storyVersion"":2,""task"":6,""toTask"":0,""toStory"":0,""title"":""\u6210\u679c\u5c55\u793a\u9875\u9762\u95ee\u9898"",""keywords"":"""",""severity"":3,""pri"":1,""type"":""codeerror"",""os"":"""",""browser"":"""",""hardware"":"""",""found"":"""",""steps"":""\u003Cp\u003E[\u6b65\ [...]
-3,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":2,""project"":7,""product"":3,""injection"":0,""identify"":0,""branch"":0,""module"":9,""execution"":1,""plan"":1,""story"":2,""storyVersion"":1,""task"":15,""toTask"":0,""toStory"":0,""title"":""\u65b0\u95fb\u4e2d\u5fc3\u9875\u9762\u95ee\u9898"",""keywords"":""hh"",""severity"":3,""pri"":2,""type"":""codeerror"",""os"":"",windows"",""browser"":"",chrome"",""hardware"":"""",""found"":"""",""steps"":""\u00 [...]
-4,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":1,""project"":7,""product"":3,""injection"":0,""identify"":0,""branch"":0,""module"":8,""execution"":1,""plan"":0,""story"":1,""storyVersion"":1,""task"":1,""toTask"":0,""toStory"":0,""title"":""\u9996\u9875\u9875\u9762\u95ee\u9898"",""keywords"":"""",""severity"":3,""pri"":1,""type"":""codeerror"",""os"":"""",""browser"":"""",""hardware"":"""",""found"":"""",""steps"":""\u003Cp\u003E[\u6b65\u9aa4]\u8fdb\ [...]
-5,"{""ConnectionId"":2,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":6,""project"":7,""product"":3,""injection"":0,""identify"":0,""branch"":0,""module"":9,""execution"":1,""plan"":1,""story"":2,""storyVersion"":1,""task"":15,""toTask"":0,""toStory"":0,""title"":""\u65b0\u95fb\u4e2d\u5fc3\u9875\u9762\u95ee\u9898"",""keywords"":""hh"",""severity"":3,""pri"":2,""type"":""codeerror"",""os"":"",windows"",""browser"":"",chrome"",""hardware"":"""",""found"":"""",""steps"":""\u00 [...]
-6,"{""ConnectionId"":3,""ProductId"":3,""ExecutionId"":11,""ProjectId"":1}","{""id"":5,""project"":7,""product"":3,""injection"":0,""identify"":0,""branch"":0,""module"":8,""execution"":1,""plan"":0,""story"":1,""storyVersion"":1,""task"":1,""toTask"":0,""toStory"":0,""title"":""\u9996\u9875\u9875\u9762\u95ee\u9898"",""keywords"":"""",""severity"":3,""pri"":1,""type"":""codeerror"",""os"":"""",""browser"":"""",""hardware"":"""",""found"":"""",""steps"":""\u003Cp\u003E[\u6b65\u9aa4]\u8fdb [...]
+1,"{""ConnectionId"":1,""ProductId"":3,""ProjectId"":1}","{""id"":4,""project"":7,""product"":3,""injection"":0,""identify"":0,""branch"":0,""module"":11,""execution"":1,""plan"":0,""story"":4,""storyVersion"":1,""task"":9,""toTask"":0,""toStory"":0,""title"":""\u552e\u540e\u670d\u52a1\u9875\u9762\u95ee\u9898"",""keywords"":"""",""severity"":3,""pri"":1,""type"":""codeerror"",""os"":"""",""browser"":"""",""hardware"":"""",""found"":"""",""steps"":""\u003Cp\u003E[\u6b65\u9aa4]\u8fdb\u5165 [...]
+2,"{""ConnectionId"":1,""ProductId"":3,""ProjectId"":1}","{""id"":3,""project"":7,""product"":3,""injection"":0,""identify"":0,""branch"":0,""module"":10,""execution"":1,""plan"":0,""story"":3,""storyVersion"":2,""task"":6,""toTask"":0,""toStory"":0,""title"":""\u6210\u679c\u5c55\u793a\u9875\u9762\u95ee\u9898"",""keywords"":"""",""severity"":3,""pri"":1,""type"":""codeerror"",""os"":"""",""browser"":"""",""hardware"":"""",""found"":"""",""steps"":""\u003Cp\u003E[\u6b65\u9aa4]\u8fdb\u5165 [...]
+3,"{""ConnectionId"":1,""ProductId"":3,""ProjectId"":1}","{""id"":2,""project"":7,""product"":3,""injection"":0,""identify"":0,""branch"":0,""module"":9,""execution"":1,""plan"":1,""story"":2,""storyVersion"":1,""task"":15,""toTask"":0,""toStory"":0,""title"":""\u65b0\u95fb\u4e2d\u5fc3\u9875\u9762\u95ee\u9898"",""keywords"":""hh"",""severity"":3,""pri"":2,""type"":""codeerror"",""os"":"",windows"",""browser"":"",chrome"",""hardware"":"""",""found"":"""",""steps"":""\u003Cp\u003E[\u6b65\u [...]
+4,"{""ConnectionId"":1,""ProductId"":3,""ProjectId"":1}","{""id"":1,""project"":7,""product"":3,""injection"":0,""identify"":0,""branch"":0,""module"":8,""execution"":1,""plan"":0,""story"":1,""storyVersion"":1,""task"":1,""toTask"":0,""toStory"":0,""title"":""\u9996\u9875\u9875\u9762\u95ee\u9898"",""keywords"":"""",""severity"":3,""pri"":1,""type"":""codeerror"",""os"":"""",""browser"":"""",""hardware"":"""",""found"":"""",""steps"":""\u003Cp\u003E[\u6b65\u9aa4]\u8fdb\u5165\u9996\u9875\ [...]
+5,"{""ConnectionId"":2,""ProductId"":3,""ProjectId"":1}","{""id"":6,""project"":7,""product"":3,""injection"":0,""identify"":0,""branch"":0,""module"":9,""execution"":1,""plan"":1,""story"":2,""storyVersion"":1,""task"":15,""toTask"":0,""toStory"":0,""title"":""\u65b0\u95fb\u4e2d\u5fc3\u9875\u9762\u95ee\u9898"",""keywords"":""hh"",""severity"":3,""pri"":2,""type"":""codeerror"",""os"":"",windows"",""browser"":"",chrome"",""hardware"":"""",""found"":"""",""steps"":""\u003Cp\u003E[\u6b65\u [...]
+6,"{""ConnectionId"":3,""ProductId"":3,""ProjectId"":1}","{""id"":5,""project"":7,""product"":3,""injection"":0,""identify"":0,""branch"":0,""module"":8,""execution"":1,""plan"":0,""story"":1,""storyVersion"":1,""task"":1,""toTask"":0,""toStory"":0,""title"":""\u9996\u9875\u9875\u9762\u95ee\u9898"",""keywords"":"""",""severity"":3,""pri"":1,""type"":""codeerror"",""os"":"""",""browser"":"""",""hardware"":"""",""found"":"""",""steps"":""\u003Cp\u003E[\u6b65\u9aa4]\u8fdb\u5165\u9996\u9875\ [...]
diff --git a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_departments.csv b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_departments.csv
index 88bd599d5..e7738528d 100644
--- a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_departments.csv
+++ b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_departments.csv
@@ -1,13 +1,13 @@
 id,params,data,url,input,created_at
-31,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":10,""dept"":1,""account"":""testManager"",""realname"":""\u6d4b\u8bd5\u7ecf\u7406"",""role"":""qd"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
-32,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":9,""dept"":3,""account"":""tester3"",""realname"":""\u6d4b\u8bd5\u4e19"",""role"":""qa"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
-33,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":8,""dept"":3,""account"":""tester2"",""realname"":""\u6d4b\u8bd5\u4e59"",""role"":""qa"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
-34,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":7,""dept"":3,""account"":""tester1"",""realname"":""\u6d4b\u8bd5\u7532"",""role"":""qa"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
-35,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":6,""dept"":2,""account"":""dev3"",""realname"":""\u5f00\u53d1\u4e19"",""role"":""dev"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
-36,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":5,""dept"":2,""account"":""dev2"",""realname"":""\u5f00\u53d1\u4e59"",""role"":""dev"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
-37,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":4,""dept"":2,""account"":""dev1"",""realname"":""\u5f00\u53d1\u7532"",""role"":""dev"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
-38,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":3,""dept"":6,""account"":""projectManager"",""realname"":""\u9879\u76ee\u7ecf\u7406"",""role"":""pm"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
-39,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":2,""dept"":5,""account"":""productManager"",""realname"":""\u4ea7\u54c1\u7ecf\u7406"",""role"":""po"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
-40,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":1,""dept"":0,""account"":""devlake"",""realname"":""devlake"",""role"":"""",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
-41,"{""ConnectionId"":2,""ProductId"":1,""ExecutionId"":4,""ProjectId"":3}","{""id"":12,""dept"":5,""account"":""productManager"",""realname"":""\u4ea7\u54c1\u7ecf\u7406"",""role"":""po"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
-42,"{""ConnectionId"":3,""ProductId"":1,""ExecutionId"":1,""ProjectId"":3}","{""id"":11,""dept"":0,""account"":""devlake"",""realname"":""devlake"",""role"":"""",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
+31,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":10,""dept"":1,""account"":""testManager"",""realname"":""\u6d4b\u8bd5\u7ecf\u7406"",""role"":""qd"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
+32,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":9,""dept"":3,""account"":""tester3"",""realname"":""\u6d4b\u8bd5\u4e19"",""role"":""qa"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
+33,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":8,""dept"":3,""account"":""tester2"",""realname"":""\u6d4b\u8bd5\u4e59"",""role"":""qa"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
+34,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":7,""dept"":3,""account"":""tester1"",""realname"":""\u6d4b\u8bd5\u7532"",""role"":""qa"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
+35,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":6,""dept"":2,""account"":""dev3"",""realname"":""\u5f00\u53d1\u4e19"",""role"":""dev"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
+36,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":5,""dept"":2,""account"":""dev2"",""realname"":""\u5f00\u53d1\u4e59"",""role"":""dev"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
+37,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":4,""dept"":2,""account"":""dev1"",""realname"":""\u5f00\u53d1\u7532"",""role"":""dev"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
+38,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":3,""dept"":6,""account"":""projectManager"",""realname"":""\u9879\u76ee\u7ecf\u7406"",""role"":""pm"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
+39,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":2,""dept"":5,""account"":""productManager"",""realname"":""\u4ea7\u54c1\u7ecf\u7406"",""role"":""po"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
+40,"{""ConnectionId"":1,""ProductId"":1,""ProjectId"":3}","{""id"":1,""dept"":0,""account"":""devlake"",""realname"":""devlake"",""role"":"""",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
+41,"{""ConnectionId"":2,""ProductId"":1,""ProjectId"":3}","{""id"":12,""dept"":5,""account"":""productManager"",""realname"":""\u4ea7\u54c1\u7ecf\u7406"",""role"":""po"",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
+42,"{""ConnectionId"":3,""ProductId"":1,""ProjectId"":3}","{""id"":11,""dept"":0,""account"":""devlake"",""realname"":""devlake"",""role"":"""",""pinyin"":"""",""email"":""""}",http://iwater.red:8000/api.php/v1/users?limit=100&page=1,null,2022-11-21 11:04:58.124
diff --git a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions.csv b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions.csv
index 500d4a4c9..c72755db1 100644
--- a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions.csv
+++ b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions.csv
@@ -1,4 +1,4 @@
 id,params,data,url,input,created_at
-1,"{""ConnectionId"":1,""ProductId"":1,""ExecutionId"":12,""ProjectId"":1}","{""id"":12,""project"":1091,""model"":"""",""type"":""sprint"",""lifetime"":""short"",""budget"":""0"",""budgetUnit"":""CNY"",""attribute"":"""",""percent"":0,""milestone"":""0"",""output"":"""",""auth"":"""",""parent"":1091,""path"":"",1091,12,"",""grade"":1,""name"":""TR5"",""code"":""0.1.3"",""begin"":""2022-11-01"",""end"":""2022-11-03"",""realBegan"":""2022-07-07"",""realEnd"":null,""days"":"""",""status"": [...]
-2,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":1,""project"":7,""model"":"""",""type"":""sprint"",""lifetime"":""short"",""budget"":""0"",""budgetUnit"":""CNY"",""attribute"":"""",""percent"":0,""milestone"":""0"",""output"":"""",""auth"":"""",""parent"":7,""path"":"",7,1,"",""grade"":1,""name"":""\u4f01\u4e1a\u7f51\u7ad9\u7b2c\u4e00\u671f"",""code"":""coWeb1"",""begin"":""2022-05-01"",""end"":""2022-06-01"",""realBegan"":null,""realEnd"":null,""days" [...]
-3,"{""ConnectionId"":2,""ProductId"":4,""ExecutionId"":1,""ProjectId"":1}","{""id"":11,""project"":7,""model"":"""",""type"":""sprint"",""lifetime"":""short"",""budget"":""0"",""budgetUnit"":""CNY"",""attribute"":"""",""percent"":0,""milestone"":""0"",""output"":"""",""auth"":"""",""parent"":7,""path"":"",7,1,"",""grade"":1,""name"":""\u4f01\u4e1a\u7f51\u7ad9\u7b2c\u4e00\u671f"",""code"":""coWeb1"",""begin"":""2022-05-01"",""end"":""2022-06-01"",""realBegan"":null,""realEnd"":null,""days [...]
+1,"{""ConnectionId"":1,""ProductId"":3,""ProjectId"":1}","{""id"":12,""project"":1,""model"":"""",""type"":""sprint"",""lifetime"":""short"",""budget"":""0"",""budgetUnit"":""CNY"",""attribute"":"""",""percent"":0,""milestone"":""0"",""output"":"""",""auth"":"""",""parent"":1091,""path"":"",1091,12,"",""grade"":1,""name"":""TR5"",""code"":""0.1.3"",""begin"":""2022-11-01"",""end"":""2022-11-03"",""realBegan"":""2022-07-07"",""realEnd"":null,""days"":"""",""status"":""done"",""subStatus"" [...]
+2,"{""ConnectionId"":1,""ProductId"":3,""ProjectId"":1}","{""id"":1,""project"":1,""model"":"""",""type"":""sprint"",""lifetime"":""short"",""budget"":""0"",""budgetUnit"":""CNY"",""attribute"":"""",""percent"":0,""milestone"":""0"",""output"":"""",""auth"":"""",""parent"":7,""path"":"",7,1,"",""grade"":1,""name"":""\u4f01\u4e1a\u7f51\u7ad9\u7b2c\u4e00\u671f"",""code"":""coWeb1"",""begin"":""2022-05-01"",""end"":""2022-06-01"",""realBegan"":null,""realEnd"":null,""days"":20,""status"":"" [...]
+3,"{""ConnectionId"":2,""ProductId"":4,""ProjectId"":1}","{""id"":11,""project"":1,""model"":"""",""type"":""sprint"",""lifetime"":""short"",""budget"":""0"",""budgetUnit"":""CNY"",""attribute"":"""",""percent"":0,""milestone"":""0"",""output"":"""",""auth"":"""",""parent"":7,""path"":"",7,1,"",""grade"":1,""name"":""\u4f01\u4e1a\u7f51\u7ad9\u7b2c\u4e00\u671f"",""code"":""coWeb1"",""begin"":""2022-05-01"",""end"":""2022-06-01"",""realBegan"":null,""realEnd"":null,""days"":20,""status"":" [...]
diff --git a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions_real.csv b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions_real.csv
deleted file mode 100644
index 9c755cee3..000000000
--- a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions_real.csv
+++ /dev/null
@@ -1,2 +0,0 @@
-id,params,data,url,input,created_at
-1,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":1,""project"":1091,""model"":"""",""type"":""sprint"",""lifetime"":""short"",""budget"":""0"",""budgetUnit"":""CNY"",""attribute"":"""",""percent"":0,""milestone"":""0"",""output"":"""",""auth"":"""",""parent"":1091,""path"":"",1091,12,"",""grade"":1,""name"":""TR5"",""code"":""0.1.3"",""begin"":""2022-11-01"",""end"":""2022-11-03"",""realBegan"":""2022-07-07"",""realEnd"":null,""days"":0,""status"":""don [...]
diff --git a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_products.csv b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_products.csv
index c8b7dc499..7e749c20a 100644
--- a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_products.csv
+++ b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_products.csv
@@ -1,3 +1,3 @@
 id,params,data,url,input,created_at
-4,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":9,""ProjectId"":1}","{""id"":3,""program"":10,""name"":""\u4ea7\u54c1\u540d\u79f01"",""code"":""\u4ea7\u54c1\u4ee3\u53f72"",""bind"":""0"",""line"":31,""type"":""normal"",""status"":""normal"",""subStatus"":"""",""desc"":""\u003Cspan style=\""background-color:#FFFFFF;\""\u003E\u4ea7\u54c1\u63cf\u8ff01\u003C\/span\u003E"",""PO"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""QD"":{""id"":1,""account"":"" [...]
-5,"{""ConnectionId"":2,""ProductId"":2,""ExecutionId"":9,""ProjectId"":1}","{""id"":3,""program"":10,""name"":""\u4ea7\u54c1\u540d\u79f01"",""code"":""\u4ea7\u54c1\u4ee3\u53f72"",""bind"":""0"",""line"":31,""type"":""normal"",""status"":""normal"",""subStatus"":"""",""desc"":""\u003Cspan style=\""background-color:#FFFFFF;\""\u003E\u4ea7\u54c1\u63cf\u8ff01\u003C\/span\u003E"",""PO"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""QD"":{""id"":1,""account"":"" [...]
+4,"{""ConnectionId"":1,""ProductId"":3,""ProjectId"":1}","{""id"":3,""program"":10,""name"":""\u4ea7\u54c1\u540d\u79f01"",""code"":""\u4ea7\u54c1\u4ee3\u53f72"",""bind"":""0"",""line"":31,""type"":""normal"",""status"":""normal"",""subStatus"":"""",""desc"":""\u003Cspan style=\""background-color:#FFFFFF;\""\u003E\u4ea7\u54c1\u63cf\u8ff01\u003C\/span\u003E"",""PO"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""QD"":{""id"":1,""account"":""devlake"",""avatar [...]
+5,"{""ConnectionId"":2,""ProductId"":2,""ProjectId"":1}","{""id"":3,""program"":10,""name"":""\u4ea7\u54c1\u540d\u79f01"",""code"":""\u4ea7\u54c1\u4ee3\u53f72"",""bind"":""0"",""line"":31,""type"":""normal"",""status"":""normal"",""subStatus"":"""",""desc"":""\u003Cspan style=\""background-color:#FFFFFF;\""\u003E\u4ea7\u54c1\u63cf\u8ff01\u003C\/span\u003E"",""PO"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""QD"":{""id"":1,""account"":""devlake"",""avatar [...]
diff --git a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_stories.csv b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_stories.csv
index 78fb04a5f..f5a929977 100644
--- a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_stories.csv
+++ b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_stories.csv
@@ -1,10 +1,10 @@
 id,params,data,url,input,created_at
-1,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":7,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":7,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u5173\u4e8e\u6211\u4eec\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""reviewing"",""subStatus"":"""",""color"":"""",""stage"":""planned"",""stagedB [...]
-2,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":6,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":6,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u5408\u4f5c\u6d3d\u8c08\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""reviewing"",""subStatus"":"""",""color"":"""",""stage"":""planned"",""stagedB [...]
-3,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":5,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":5,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u8bda\u8058\u82f1\u624d\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""reviewing"",""subStatus"":"""",""color"":"""",""stage"":""planned"",""stagedB [...]
-4,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":4,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":4,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u552e\u540e\u670d\u52a1\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""active"",""subStatus"":"""",""color"":"""",""stage"":""developed"",""stagedBy [...]
-5,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":3,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":3,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u6210\u679c\u5c55\u793a\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":0,""status"":""active"",""subStatus"":"""",""color"":"""",""stage"":""developing"",""stagedB [...]
-6,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":2,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":2,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u65b0\u95fb\u4e2d\u5fc3\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1\u3002"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""active"",""subStatus"":"""",""color"":"""",""stage"":""projected"",""st [...]
-7,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":1,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":1,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u9996\u9875\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""active"",""subStatus"":"""",""color"":"""",""stage"":""developing"",""stagedBy"":"""",""mailto" [...]
-8,"{""ConnectionId"":2,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":8,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":2,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u65b0\u95fb\u4e2d\u5fc3\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1\u3002"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""active"",""subStatus"":"""",""color"":"""",""stage"":""projected"",""st [...]
-9,"{""ConnectionId"":3,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":9,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":1,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u9996\u9875\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""active"",""subStatus"":"""",""color"":"""",""stage"":""developing"",""stagedBy"":"""",""mailto" [...]
+1,"{""ConnectionId"":1,""ProductId"":3,""ProjectId"":1}","{""id"":7,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":7,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u5173\u4e8e\u6211\u4eec\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""reviewing"",""subStatus"":"""",""color"":"""",""stage"":""planned"",""stagedBy"":"""",""mailto" [...]
+2,"{""ConnectionId"":1,""ProductId"":3,""ProjectId"":1}","{""id"":6,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":6,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u5408\u4f5c\u6d3d\u8c08\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""reviewing"",""subStatus"":"""",""color"":"""",""stage"":""planned"",""stagedBy"":"""",""mailto" [...]
+3,"{""ConnectionId"":1,""ProductId"":3,""ProjectId"":1}","{""id"":5,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":5,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u8bda\u8058\u82f1\u624d\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""reviewing"",""subStatus"":"""",""color"":"""",""stage"":""planned"",""stagedBy"":"""",""mailto" [...]
+4,"{""ConnectionId"":1,""ProductId"":3,""ProjectId"":1}","{""id"":4,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":4,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u552e\u540e\u670d\u52a1\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""active"",""subStatus"":"""",""color"":"""",""stage"":""developed"",""stagedBy"":"""",""mailto"" [...]
+5,"{""ConnectionId"":1,""ProductId"":3,""ProjectId"":1}","{""id"":3,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":3,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u6210\u679c\u5c55\u793a\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":0,""status"":""active"",""subStatus"":"""",""color"":"""",""stage"":""developing"",""stagedBy"":"""",""mailto" [...]
+6,"{""ConnectionId"":1,""ProductId"":3,""ProjectId"":1}","{""id"":2,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":2,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u65b0\u95fb\u4e2d\u5fc3\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1\u3002"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""active"",""subStatus"":"""",""color"":"""",""stage"":""projected"",""stagedBy"":"""",""ma [...]
+7,"{""ConnectionId"":1,""ProductId"":3,""ProjectId"":1}","{""id"":1,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":1,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u9996\u9875\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""active"",""subStatus"":"""",""color"":"""",""stage"":""developing"",""stagedBy"":"""",""mailto"":[],""lib"":0,""f [...]
+8,"{""ConnectionId"":2,""ProductId"":3,""ProjectId"":1}","{""id"":8,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":2,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u65b0\u95fb\u4e2d\u5fc3\u7684\u8bbe\u8ba1\u548c\u5f00\u53d1\u3002"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""active"",""subStatus"":"""",""color"":"""",""stage"":""projected"",""stagedBy"":"""",""ma [...]
+9,"{""ConnectionId"":3,""ProductId"":3,""ProjectId"":1}","{""id"":9,""vision"":""rnd"",""parent"":0,""product"":3,""branch"":0,""module"":1,""plan"":""1"",""source"":""po"",""sourceNote"":"""",""fromBug"":0,""feedback"":0,""title"":""\u9996\u9875\u8bbe\u8ba1\u548c\u5f00\u53d1"",""keywords"":"""",""type"":""story"",""category"":""feature"",""pri"":1,""estimate"":1,""status"":""active"",""subStatus"":"""",""color"":"""",""stage"":""developing"",""stagedBy"":"""",""mailto"":[],""lib"":0,""f [...]
diff --git a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_tasks.csv b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_tasks.csv
index 5b9361a62..f0640a817 100644
--- a/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_tasks.csv
+++ b/backend/plugins/zentao/e2e/raw_tables/_raw_zentao_api_tasks.csv
@@ -1,6 +1,6 @@
 id,params,data,url,input,created_at
-1,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":1,""project"":13,""parent"":0,""execution"":1,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":0,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":"" [...]
-2,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":2,""project"":13,""parent"":0,""execution"":1,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":12.1,""consumed"":2.1,""left"":10,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""accoun [...]
-4,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":3,""project"":13,""parent"":-1,""execution"":1,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":11.2,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account" [...]
-5,"{""ConnectionId"":3,""ProductId"":1,""ExecutionId"":4,""ProjectId"":1}","{""id"":2,""project"":13,""parent"":0,""execution"":9,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":12.1,""consumed"":2.1,""left"":10,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""accoun [...]
-6,"{""ConnectionId"":2,""ProductId"":1,""ExecutionId"":3,""ProjectId"":1}","{""id"":3,""project"":13,""parent"":-1,""execution"":9,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":11.2,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account" [...]
+1,"{""ConnectionId"":1,""ProductId"":3,""ProjectId"":1}","{""id"":1,""project"":1,""parent"":0,""execution"":1,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":0,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":""productManager"","" [...]
+2,"{""ConnectionId"":1,""ProductId"":3,""ProjectId"":1}","{""id"":2,""project"":1,""parent"":0,""execution"":1,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":12.1,""consumed"":2.1,""left"":10,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":""productManage [...]
+4,"{""ConnectionId"":1,""ProductId"":3,""ProjectId"":1}","{""id"":3,""project"":1,""parent"":-1,""execution"":1,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":11.2,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":""productManager" [...]
+5,"{""ConnectionId"":3,""ProductId"":1,""ProjectId"":1}","{""id"":2,""project"":1,""parent"":0,""execution"":9,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":12.1,""consumed"":2.1,""left"":10,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":""productManage [...]
+6,"{""ConnectionId"":2,""ProductId"":1,""ProjectId"":1}","{""id"":3,""project"":1,""parent"":-1,""execution"":9,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":11.2,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":""productManager" [...]
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_executions.csv b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_executions.csv
index 4a5f7f367..db4ccd6f4 100644
--- a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_executions.csv
+++ b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_executions.csv
@@ -1,2 +1,3 @@
 connection_id,id,project,model,type,lifetime,budget,budget_unit,attribute,percent,milestone,output,auth,parent,path,grade,name,code,plan_begin,plan_end,real_began,real_end,status,sub_status,pri,description,version,parent_version,plan_duration,real_duration,opened_by_id,opened_date,opened_version,last_edited_by_id,last_edited_date,closed_by_id,closed_date,canceled_by_id,canceled_date,suspended_date,po_id,pm_id,qd_id,rd_id,team,acl,order_in,vision,display_cards,fluid_board,deleted,total_ho [...]
-1,1,7,,sprint,short,0,CNY,,0,0,,,7,",7,1,",1,企业网站第一期,coWeb1,2022-05-01T00:00:00.000+00:00,2022-06-01T00:00:00.000+00:00,,,doing,,1,开发企业网站的基本雏形。<br />,0,0,0,0,0,,,1,2022-11-21T06:00:56.000+00:00,0,,0,,,2,3,10,2,公司开发团队,open,5,rnd,0,0,0,11753,52,51.5,27.5,0,65.2,0
+1,1,1,,sprint,short,0,CNY,,0,0,,,7,",7,1,",1,企业网站第一期,coWeb1,2022-05-01T00:00:00.000+00:00,2022-06-01T00:00:00.000+00:00,,,doing,,1,开发企业网站的基本雏形。<br />,0,0,0,0,0,,,1,2022-11-21T06:00:56.000+00:00,0,,0,,,2,3,10,2,公司开发团队,open,5,rnd,0,0,0,11753,52,51.5,27.5,1,65.2,0
+1,12,1,,sprint,short,0,CNY,,0,0,,,1091,",1091,12,",1,TR5,0.1.3,2022-11-01T00:00:00.000+00:00,2022-11-03T00:00:00.000+00:00,2022-07-07T00:00:00.000+00:00,,done,,1,,0,0,24,0,6,2021-05-27T07:16:59.000+00:00,15.0.rc3,6,2022-11-15T08:22:09.000+00:00,0,,0,,,0,6,0,0,Windows组,open,5,rnd,0,1,0,0,8411,11564.5,0,1,100,0
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_tasks.csv b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_tasks.csv
index 5680ee342..40337d85f 100644
--- a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_tasks.csv
+++ b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_tasks.csv
@@ -1,4 +1,4 @@
 connection_id,id,project,parent,execution,module,design,story,story_version,design_version,from_bug,feedback,from_issue,name,type,mode,pri,estimate,consumed,deadline,status,sub_status,color,description,version,opened_by_id,opened_by_name,opened_date,assigned_to_id,assigned_to_name,assigned_date,est_started,real_started,finished_id,finished_date,finished_list,canceled_id,canceled_date,closed_by_id,closed_date,plan_duration,real_duration,closed_reason,last_edited_id,last_edited_date,activa [...]
-1,1,13,0,1,0,0,0,1,0,0,0,0,任务名称,devel,,3,0,0,2022-10-01,wait,,,任务描述<span> </span><br /><div><br /></div>,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,21.11
-1,2,13,0,1,0,0,0,1,0,0,0,0,任务名称,devel,,3,12.1,2.1,2022-10-01,wait,,,任务描述<span> </span><br /><div><br /></div>,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,3
-1,3,13,-1,1,0,0,0,1,0,0,0,0,任务名称,devel,,3,11.2,0,2022-10-01,wait,,,任务描述<span> </span><br /><div><br /></div>,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,43.22121
+1,1,1,0,1,0,0,0,1,0,0,0,0,任务名称,devel,,3,0,0,2022-10-01,wait,,,任务描述<span> </span><br /><div><br /></div>,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,21.11
+1,2,1,0,1,0,0,0,1,0,0,0,0,任务名称,devel,,3,12.1,2.1,2022-10-01,wait,,,任务描述<span> </span><br /><div><br /></div>,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,3
+1,3,1,-1,1,0,0,0,1,0,0,0,0,任务名称,devel,,3,11.2,0,2022-10-01,wait,,,任务描述<span> </span><br /><div><br /></div>,1,1,devlake,2022-09-19T01:50:37.000+00:00,5,开发乙,2022-09-19T01:50:37.000+00:00,2022-09-20,,0,,,0,,0,,0,0,,0,,,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,43.22121
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/boards_execution.csv b/backend/plugins/zentao/e2e/snapshot_tables/boards_execution.csv
deleted file mode 100644
index e596c62f9..000000000
--- a/backend/plugins/zentao/e2e/snapshot_tables/boards_execution.csv
+++ /dev/null
@@ -1,2 +0,0 @@
-id,name,description,url,created_date,type
-zentao:ZentaoExecution:1:1,企业网站第一期,开发企业网站的基本雏形。<br />,",7,1,",,sprint
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/execution_board_sprint.csv b/backend/plugins/zentao/e2e/snapshot_tables/execution_board_sprint.csv
new file mode 100644
index 000000000..0760d8741
--- /dev/null
+++ b/backend/plugins/zentao/e2e/snapshot_tables/execution_board_sprint.csv
@@ -0,0 +1,3 @@
+board_id,sprint_id
+zentao:ZentaoProject:1:1,zentao:ZentaoExecution:1:1
+zentao:ZentaoProject:1:12,zentao:ZentaoExecution:1:12
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/execution_sprint.csv b/backend/plugins/zentao/e2e/snapshot_tables/execution_sprint.csv
new file mode 100644
index 000000000..81e23be75
--- /dev/null
+++ b/backend/plugins/zentao/e2e/snapshot_tables/execution_sprint.csv
@@ -0,0 +1,3 @@
+id,name,url,status,started_date,ended_date,completed_date,original_board_id
+zentao:ZentaoExecution:1:1,企业网站第一期,",7,1,",ACTIVE,,,,zentao:ZentaoProject:1:1
+zentao:ZentaoExecution:1:12,TR5,",1091,12,",CLOSED,2022-07-07T00:00:00.000+00:00,,,zentao:ZentaoProject:1:12
diff --git a/backend/plugins/zentao/e2e/story_test.go b/backend/plugins/zentao/e2e/story_test.go
index c14695459..14ca04a4f 100644
--- a/backend/plugins/zentao/e2e/story_test.go
+++ b/backend/plugins/zentao/e2e/story_test.go
@@ -37,7 +37,6 @@ func TestZentaoStoryDataFlow(t *testing.T) {
 			ConnectionId: 1,
 			ProjectId:    1,
 			ProductId:    3,
-			ExecutionId:  1,
 		},
 	}
 
diff --git a/backend/plugins/zentao/e2e/task_test.go b/backend/plugins/zentao/e2e/task_test.go
index 1debcdf2b..a7967570a 100644
--- a/backend/plugins/zentao/e2e/task_test.go
+++ b/backend/plugins/zentao/e2e/task_test.go
@@ -37,7 +37,6 @@ func TestZentaoTaskDataFlow(t *testing.T) {
 			ConnectionId: 1,
 			ProjectId:    1,
 			ProductId:    3,
-			ExecutionId:  1,
 		},
 	}
 
diff --git a/backend/plugins/zentao/impl/impl.go b/backend/plugins/zentao/impl/impl.go
index 66359d068..617bf1fb1 100644
--- a/backend/plugins/zentao/impl/impl.go
+++ b/backend/plugins/zentao/impl/impl.go
@@ -34,7 +34,8 @@ var _ plugin.PluginMeta = (*Zentao)(nil)
 var _ plugin.PluginInit = (*Zentao)(nil)
 var _ plugin.PluginTask = (*Zentao)(nil)
 var _ plugin.PluginApi = (*Zentao)(nil)
-var _ plugin.PluginBlueprintV100 = (*Zentao)(nil)
+
+// var _ plugin.CompositePluginBlueprintV200 = (*Zentao)(nil)
 var _ plugin.CloseablePluginTask = (*Zentao)(nil)
 
 type Zentao struct{}
@@ -50,9 +51,8 @@ func (p Zentao) Init(basicRes context.BasicRes) errors.Error {
 
 func (p Zentao) SubTaskMetas() []plugin.SubTaskMeta {
 	return []plugin.SubTaskMeta{
-		tasks.CollectProductMeta,
-		tasks.ExtractProductMeta,
 		tasks.ConvertProductMeta,
+		tasks.ConvertProjectMeta,
 		tasks.CollectExecutionMeta,
 		tasks.ExtractExecutionMeta,
 		tasks.ConvertExecutionMeta,
@@ -123,11 +123,28 @@ func (p Zentao) ApiResources() map[string]map[string]plugin.ApiResourceHandler {
 			"PATCH":  api.PatchConnection,
 			"DELETE": api.DeleteConnection,
 		},
+		"connections/:connectionId/product/scopes": {
+			"PUT": api.PutProductScope,
+		},
+		"connections/:connectionId/project/scopes": {
+			"PUT": api.PutProjectScope,
+		},
+		"connections/:connectionId/scopes/product/:scopeId": {
+			"GET":   api.GetProductScope,
+			"PATCH": api.UpdateProductScope,
+		},
+		"connections/:connectionId/scopes/project/:scopeId": {
+			"GET":   api.GetProjectScope,
+			"PATCH": api.UpdateProjectScope,
+		},
+		"connections/:connectionId/remote-scopes": {
+			"GET": api.RemoteScopes,
+		},
 	}
 }
 
-func (p Zentao) MakePipelinePlan(connectionId uint64, scope []*plugin.BlueprintScopeV100) (plugin.PipelinePlan, errors.Error) {
-	return api.MakePipelinePlan(p.SubTaskMetas(), connectionId, scope)
+func (p Zentao) MakeDataSourcePipelinePlanV200(connectionId uint64, scopes []*plugin.BlueprintScopeV200, syncPolicy plugin.BlueprintSyncPolicy) (pp plugin.PipelinePlan, sc []plugin.Scope, err errors.Error) {
+	return api.MakeDataSourcePipelinePlanV200(p.SubTaskMetas(), connectionId, scopes, &syncPolicy)
 }
 
 func (p Zentao) Close(taskCtx plugin.TaskContext) errors.Error {
diff --git a/backend/plugins/zentao/models/product.go b/backend/plugins/zentao/models/product.go
index ced4f0735..5f1ab2af6 100644
--- a/backend/plugins/zentao/models/product.go
+++ b/backend/plugins/zentao/models/product.go
@@ -18,7 +18,9 @@ limitations under the License.
 package models
 
 import (
+	"fmt"
 	"github.com/apache/incubator-devlake/core/models/common"
+	"github.com/apache/incubator-devlake/core/plugin"
 	helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
 )
 
@@ -66,41 +68,91 @@ type ZentaoProductRes struct {
 	CaseReview bool    `json:"caseReview"`
 }
 
+func getAccountId(account *ZentaoAccount) int64 {
+	if account != nil {
+		return account.ID
+	}
+	return 0
+}
+
+func (res ZentaoProductRes) ConvertApiScope() plugin.ToolLayerScope {
+	return &ZentaoProduct{
+		Id:             res.ID,
+		Program:        res.Program,
+		Name:           res.Name,
+		Code:           res.Code,
+		Bind:           res.Bind,
+		Line:           res.Line,
+		Type:           `product/` + res.Type,
+		Status:         res.Status,
+		SubStatus:      res.SubStatus,
+		Description:    res.Description,
+		POId:           getAccountId(res.PO),
+		QDId:           getAccountId(res.QD),
+		RDId:           getAccountId(res.RD),
+		Acl:            res.Acl,
+		Reviewer:       res.Reviewer,
+		CreatedById:    getAccountId(res.CreatedBy),
+		CreatedDate:    res.CreatedDate,
+		CreatedVersion: res.CreatedVersion,
+		OrderIn:        res.OrderIn,
+		Deleted:        res.Deleted,
+		Plans:          res.Plans,
+		Releases:       res.Releases,
+		Builds:         res.Builds,
+		Cases:          res.Cases,
+		Projects:       res.Projects,
+		Executions:     res.Executions,
+		Bugs:           res.Bugs,
+		Docs:           res.Docs,
+		Progress:       res.Progress,
+		CaseReview:     res.CaseReview,
+	}
+}
+
 type ZentaoProduct struct {
-	ConnectionId   uint64 `gorm:"primaryKey;type:BIGINT  NOT NULL"`
-	Id             int64  `json:"id" gorm:"primaryKey;type:BIGINT  NOT NULL"`
-	Program        int    `json:"program"`
-	Name           string `json:"name"`
-	Code           string `json:"code"`
-	Bind           string `json:"bind"`
-	Line           int    `json:"line"`
-	Type           string `json:"type"`
-	Status         string `json:"status"`
-	SubStatus      string `json:"subStatus"`
-	Description    string `json:"desc"`
-	POId           int64
-	QDId           int64
-	RDId           int64
-	Acl            string `json:"acl"`
-	Reviewer       string `json:"reviewer"`
-	CreatedById    int64
-	CreatedDate    *helper.Iso8601Time `json:"createdDate"`
-	CreatedVersion string              `json:"createdVersion"`
-	OrderIn        int                 `json:"order"`
-	Deleted        string              `json:"deleted"`
-	Plans          int                 `json:"plans"`
-	Releases       int                 `json:"releases"`
-	Builds         int                 `json:"builds"`
-	Cases          int                 `json:"cases"`
-	Projects       int                 `json:"projects"`
-	Executions     int                 `json:"executions"`
-	Bugs           int                 `json:"bugs"`
-	Docs           int                 `json:"docs"`
-	Progress       float64             `json:"progress"`
-	CaseReview     bool                `json:"caseReview"`
-	common.NoPKModel
+	common.NoPKModel `json:"-"`
+	ConnectionId     uint64 `json:"connectionid" gorm:"primaryKey;type:BIGINT  NOT NULL"`
+	Id               int64  `json:"id" gorm:"primaryKey;type:BIGINT  NOT NULL"`
+	Program          int    `json:"program"`
+	Name             string `json:"name"`
+	Code             string `json:"code"`
+	Bind             string `json:"bind"`
+	Line             int    `json:"line"`
+	Type             string `json:"type"`
+	Status           string `json:"status"`
+	SubStatus        string `json:"subStatus"`
+	Description      string `json:"desc"`
+	POId             int64
+	QDId             int64
+	RDId             int64
+	Acl              string `json:"acl"`
+	Reviewer         string `json:"reviewer"`
+	CreatedById      int64
+	CreatedDate      *helper.Iso8601Time `json:"createdDate"`
+	CreatedVersion   string              `json:"createdVersion"`
+	OrderIn          int                 `json:"order"`
+	Deleted          string              `json:"deleted"`
+	Plans            int                 `json:"plans"`
+	Releases         int                 `json:"releases"`
+	Builds           int                 `json:"builds"`
+	Cases            int                 `json:"cases"`
+	Projects         int                 `json:"projects"`
+	Executions       int                 `json:"executions"`
+	Bugs             int                 `json:"bugs"`
+	Docs             int                 `json:"docs"`
+	Progress         float64             `json:"progress"`
+	CaseReview       bool                `json:"caseReview"`
 }
 
 func (ZentaoProduct) TableName() string {
 	return "_tool_zentao_products"
 }
+
+func (p ZentaoProduct) ScopeId() string {
+	return fmt.Sprintf(`product/%d`, p.Id)
+}
+
+func (p ZentaoProduct) ScopeName() string {
+	return p.Name
+}
diff --git a/backend/plugins/zentao/models/project.go b/backend/plugins/zentao/models/project.go
index 89945a8f5..0c4d3821f 100644
--- a/backend/plugins/zentao/models/project.go
+++ b/backend/plugins/zentao/models/project.go
@@ -18,47 +18,49 @@ limitations under the License.
 package models
 
 import (
+	"fmt"
 	"github.com/apache/incubator-devlake/core/models/common"
+	"github.com/apache/incubator-devlake/core/plugin"
 	helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
 )
 
 type ZentaoProject struct {
-	common.NoPKModel
-	ConnectionId  uint64              `gorm:"primaryKey;type:BIGINT  NOT NULL"`
-	ID            int64               `json:"id" gorm:"primaryKey;type:BIGINT  NOT NULL"`
-	Project       int64               `json:"project"`
-	Model         string              `json:"model"`
-	Type          string              `json:"type"`
-	Lifetime      string              `json:"lifetime"`
-	Budget        string              `json:"budget"`
-	BudgetUnit    string              `json:"budgetUnit"`
-	Attribute     string              `json:"attribute"`
-	Percent       int                 `json:"percent"`
-	Milestone     string              `json:"milestone"`
-	Output        string              `json:"output"`
-	Auth          string              `json:"auth"`
-	Parent        int64               `json:"parent"`
-	Path          string              `json:"path"`
-	Grade         int                 `json:"grade"`
-	Name          string              `json:"name"`
-	Code          string              `json:"code"`
-	PlanBegin     *helper.Iso8601Time `json:"begin"`
-	PlanEnd       *helper.Iso8601Time `json:"end"`
-	RealBegan     *helper.Iso8601Time `json:"realBegan"`
-	RealEnd       *helper.Iso8601Time `json:"realEnd"`
-	Days          int                 `json:"days"`
-	Status        string              `json:"status"`
-	SubStatus     string              `json:"subStatus"`
-	Pri           string              `json:"pri"`
-	Description   string              `json:"desc"`
-	Version       int                 `json:"version"`
-	ParentVersion int                 `json:"parentVersion"`
-	PlanDuration  int                 `json:"planDuration"`
-	RealDuration  int                 `json:"realDuration"`
+	common.NoPKModel `json:"-"`
+	ConnectionId     uint64              `json:"connectionid" gorm:"primaryKey;type:BIGINT  NOT NULL"`
+	Id               int64               `json:"id" gorm:"primaryKey;type:BIGINT  NOT NULL"`
+	Project          int64               `json:"project"`
+	Model            string              `json:"model"`
+	Type             string              `json:"type"`
+	Lifetime         string              `json:"lifetime"`
+	Budget           string              `json:"budget"`
+	BudgetUnit       string              `json:"budgetUnit"`
+	Attribute        string              `json:"attribute"`
+	Percent          int                 `json:"percent"`
+	Milestone        string              `json:"milestone"`
+	Output           string              `json:"output"`
+	Auth             string              `json:"auth"`
+	Parent           int64               `json:"parent"`
+	Path             string              `json:"path"`
+	Grade            int                 `json:"grade"`
+	Name             string              `json:"name"`
+	Code             string              `json:"code"`
+	PlanBegin        *helper.Iso8601Time `json:"begin"`
+	PlanEnd          *helper.Iso8601Time `json:"end"`
+	RealBegan        *helper.Iso8601Time `json:"realBegan"`
+	RealEnd          *helper.Iso8601Time `json:"realEnd"`
+	Days             int                 `json:"days"`
+	Status           string              `json:"status"`
+	SubStatus        string              `json:"subStatus"`
+	Pri              string              `json:"pri"`
+	Description      string              `json:"desc"`
+	Version          int                 `json:"version"`
+	ParentVersion    int                 `json:"parentVersion"`
+	PlanDuration     int                 `json:"planDuration"`
+	RealDuration     int                 `json:"realDuration"`
 	//OpenedBy       string    `json:"openedBy"`
-	OpenedDate     *helper.Iso8601Time `json:"openedDate"`
-	OpenedVersion  string              `json:"openedVersion"`
-	LastEditedBy   string              `json:"lastEditedBy"`
+	OpenedDate    *helper.Iso8601Time `json:"openedDate"`
+	OpenedVersion string              `json:"openedVersion"`
+	//LastEditedBy   string              `json:"lastEditedBy"`
 	LastEditedDate *helper.Iso8601Time `json:"lastEditedDate"`
 	ClosedBy       string              `json:"closedBy"`
 	ClosedDate     *helper.Iso8601Time `json:"closedDate"`
@@ -111,3 +113,15 @@ type Hours struct {
 func (ZentaoProject) TableName() string {
 	return "_tool_zentao_projects"
 }
+
+func (p ZentaoProject) ScopeId() string {
+	return fmt.Sprintf(`project/%d`, p.Id)
+}
+
+func (p ZentaoProject) ScopeName() string {
+	return p.Name
+}
+
+func (p ZentaoProject) ConvertApiScope() plugin.ToolLayerScope {
+	return p
+}
diff --git a/backend/plugins/zentao/tasks/account_collector.go b/backend/plugins/zentao/tasks/account_collector.go
index 176073cc5..7294f449f 100644
--- a/backend/plugins/zentao/tasks/account_collector.go
+++ b/backend/plugins/zentao/tasks/account_collector.go
@@ -39,7 +39,6 @@ func CollectAccount(taskCtx plugin.SubTaskContext) errors.Error {
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
 				ProductId:    data.Options.ProductId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProjectId:    data.Options.ProjectId,
 			},
 			Table: RAW_ACCOUNT_TABLE,
@@ -77,4 +76,5 @@ var CollectAccountMeta = plugin.SubTaskMeta{
 	EntryPoint:       CollectAccount,
 	EnabledByDefault: true,
 	Description:      "Collect Account data from Zentao api",
+	DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
 }
diff --git a/backend/plugins/zentao/tasks/account_convertor.go b/backend/plugins/zentao/tasks/account_convertor.go
index eb7b44e15..96b33d417 100644
--- a/backend/plugins/zentao/tasks/account_convertor.go
+++ b/backend/plugins/zentao/tasks/account_convertor.go
@@ -60,7 +60,6 @@ func ConvertAccount(taskCtx plugin.SubTaskContext) errors.Error {
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
 				ProductId:    data.Options.ProductId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProjectId:    data.Options.ProjectId,
 			},
 			Table: RAW_ACCOUNT_TABLE,
diff --git a/backend/plugins/zentao/tasks/account_extractor.go b/backend/plugins/zentao/tasks/account_extractor.go
index 939c62bc7..476cdb8cf 100644
--- a/backend/plugins/zentao/tasks/account_extractor.go
+++ b/backend/plugins/zentao/tasks/account_extractor.go
@@ -43,7 +43,6 @@ func ExtractAccount(taskCtx plugin.SubTaskContext) errors.Error {
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
 				ProductId:    data.Options.ProductId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProjectId:    data.Options.ProjectId,
 			},
 			Table: RAW_ACCOUNT_TABLE,
diff --git a/backend/plugins/zentao/tasks/bug_collector.go b/backend/plugins/zentao/tasks/bug_collector.go
index 0b8ca0255..91758442b 100644
--- a/backend/plugins/zentao/tasks/bug_collector.go
+++ b/backend/plugins/zentao/tasks/bug_collector.go
@@ -33,13 +33,15 @@ var _ plugin.SubTaskEntryPoint = CollectBug
 
 func CollectBug(taskCtx plugin.SubTaskContext) errors.Error {
 	data := taskCtx.GetData().(*ZentaoTaskData)
+	if data.Options.ProductId == 0 {
+		return nil
+	}
 	collector, err := api.NewApiCollector(api.ApiCollectorArgs{
 		RawDataSubTaskArgs: api.RawDataSubTaskArgs{
 			Ctx: taskCtx,
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
 				ProductId:    data.Options.ProductId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProjectId:    data.Options.ProjectId,
 			},
 			Table: RAW_BUG_TABLE,
@@ -77,4 +79,5 @@ var CollectBugMeta = plugin.SubTaskMeta{
 	EntryPoint:       CollectBug,
 	EnabledByDefault: true,
 	Description:      "Collect Bug data from Zentao api",
+	DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
 }
diff --git a/backend/plugins/zentao/tasks/bug_convertor.go b/backend/plugins/zentao/tasks/bug_convertor.go
index fefd7145f..67f89a932 100644
--- a/backend/plugins/zentao/tasks/bug_convertor.go
+++ b/backend/plugins/zentao/tasks/bug_convertor.go
@@ -63,7 +63,6 @@ func ConvertBug(taskCtx plugin.SubTaskContext) errors.Error {
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
 				ProductId:    data.Options.ProductId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProjectId:    data.Options.ProjectId,
 			},
 			Table: RAW_BUG_TABLE,
diff --git a/backend/plugins/zentao/tasks/bug_extractor.go b/backend/plugins/zentao/tasks/bug_extractor.go
index e07ed3377..9855d843e 100644
--- a/backend/plugins/zentao/tasks/bug_extractor.go
+++ b/backend/plugins/zentao/tasks/bug_extractor.go
@@ -43,7 +43,6 @@ func ExtractBug(taskCtx plugin.SubTaskContext) errors.Error {
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
 				ProductId:    data.Options.ProductId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProjectId:    data.Options.ProjectId,
 			},
 			Table: RAW_BUG_TABLE,
diff --git a/backend/plugins/zentao/tasks/department_collector.go b/backend/plugins/zentao/tasks/department_collector.go
index 294673ba2..aa2fc103a 100644
--- a/backend/plugins/zentao/tasks/department_collector.go
+++ b/backend/plugins/zentao/tasks/department_collector.go
@@ -39,7 +39,6 @@ func CollectDepartment(taskCtx plugin.SubTaskContext) errors.Error {
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
 				ProductId:    data.Options.ProductId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProjectId:    data.Options.ProjectId,
 			},
 			Table: RAW_DEPARTMENT_TABLE,
@@ -76,4 +75,5 @@ var CollectDepartmentMeta = plugin.SubTaskMeta{
 	EntryPoint:       CollectDepartment,
 	EnabledByDefault: true,
 	Description:      "Collect Department data from Zentao api",
+	DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
 }
diff --git a/backend/plugins/zentao/tasks/department_convertor.go b/backend/plugins/zentao/tasks/department_convertor.go
index 24289b4aa..caa74c1bc 100644
--- a/backend/plugins/zentao/tasks/department_convertor.go
+++ b/backend/plugins/zentao/tasks/department_convertor.go
@@ -59,7 +59,6 @@ func ConvertDepartment(taskCtx plugin.SubTaskContext) errors.Error {
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
 				ProductId:    data.Options.ProductId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProjectId:    data.Options.ProjectId,
 			},
 			Table: RAW_DEPARTMENT_TABLE,
diff --git a/backend/plugins/zentao/tasks/department_extractor.go b/backend/plugins/zentao/tasks/department_extractor.go
index 0126fe879..e2d389511 100644
--- a/backend/plugins/zentao/tasks/department_extractor.go
+++ b/backend/plugins/zentao/tasks/department_extractor.go
@@ -43,7 +43,6 @@ func ExtractDepartment(taskCtx plugin.SubTaskContext) errors.Error {
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
 				ProductId:    data.Options.ProductId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProjectId:    data.Options.ProjectId,
 			},
 			Table: RAW_DEPARTMENT_TABLE,
diff --git a/backend/plugins/zentao/tasks/execution_collector.go b/backend/plugins/zentao/tasks/execution_collector.go
index c9c3e123b..1218f7fb3 100644
--- a/backend/plugins/zentao/tasks/execution_collector.go
+++ b/backend/plugins/zentao/tasks/execution_collector.go
@@ -23,7 +23,6 @@ import (
 	"github.com/apache/incubator-devlake/core/errors"
 	"github.com/apache/incubator-devlake/core/plugin"
 	"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
-	"io"
 	"net/http"
 	"net/url"
 )
@@ -34,7 +33,7 @@ var _ plugin.SubTaskEntryPoint = CollectExecution
 
 func CollectExecution(taskCtx plugin.SubTaskContext) errors.Error {
 	data := taskCtx.GetData().(*ZentaoTaskData)
-	if data.Options.ExecutionId == 0 {
+	if data.Options.ProjectId == 0 {
 		return nil
 	}
 	collector, err := api.NewApiCollector(api.ApiCollectorArgs{
@@ -43,13 +42,12 @@ func CollectExecution(taskCtx plugin.SubTaskContext) errors.Error {
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
 				ProductId:    data.Options.ProductId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProjectId:    data.Options.ProjectId,
 			},
 			Table: RAW_EXECUTION_TABLE,
 		},
 		ApiClient:   data.ApiClient,
-		UrlTemplate: "executions/{{ .Params.ExecutionId }}",
+		UrlTemplate: "projects/{{ .Params.ProjectId }}/executions",
 		Query: func(reqData *api.RequestData) (url.Values, errors.Error) {
 			query := url.Values{}
 			query.Set("page", fmt.Sprintf("%v", reqData.Pager.Page))
@@ -57,12 +55,14 @@ func CollectExecution(taskCtx plugin.SubTaskContext) errors.Error {
 			return query, nil
 		},
 		ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) {
-			body, err := io.ReadAll(res.Body)
+			var data struct {
+				Executions []json.RawMessage `json:"executions"`
+			}
+			err := api.UnmarshalResponse(res, &data)
 			if err != nil {
-				return nil, errors.Default.Wrap(err, "error reading endpoint response by Zentao execution collector")
+				return nil, errors.Default.Wrap(err, "error reading endpoint response by Zentao bug collector")
 			}
-			res.Body.Close()
-			return []json.RawMessage{body}, nil
+			return data.Executions, nil
 		},
 	})
 	if err != nil {
@@ -77,4 +77,5 @@ var CollectExecutionMeta = plugin.SubTaskMeta{
 	EntryPoint:       CollectExecution,
 	EnabledByDefault: true,
 	Description:      "Collect Execution data from Zentao api",
+	DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
 }
diff --git a/backend/plugins/zentao/tasks/execution_convertor.go b/backend/plugins/zentao/tasks/execution_convertor.go
index fbc78d563..4c9866189 100644
--- a/backend/plugins/zentao/tasks/execution_convertor.go
+++ b/backend/plugins/zentao/tasks/execution_convertor.go
@@ -42,11 +42,11 @@ var ConvertExecutionMeta = plugin.SubTaskMeta{
 func ConvertExecutions(taskCtx plugin.SubTaskContext) errors.Error {
 	data := taskCtx.GetData().(*ZentaoTaskData)
 	db := taskCtx.GetDal()
-	boardIdGen := didgen.NewDomainIdGenerator(&models.ZentaoExecution{})
+	executionIdGen := didgen.NewDomainIdGenerator(&models.ZentaoExecution{})
+	projectIdGen := didgen.NewDomainIdGenerator(&models.ZentaoProject{})
 	cursor, err := db.Cursor(
 		dal.From(&models.ZentaoExecution{}),
-		dal.Where(`_tool_zentao_executions.id = ? and 
-			_tool_zentao_executions.connection_id = ?`, data.Options.ExecutionId, data.Options.ConnectionId),
+		dal.Where(`project_id = ? and connection_id = ?`, data.Options.ProjectId, data.Options.ConnectionId),
 	)
 	if err != nil {
 		return err
@@ -60,7 +60,6 @@ func ConvertExecutions(taskCtx plugin.SubTaskContext) errors.Error {
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
 				ProductId:    data.Options.ProductId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProjectId:    data.Options.ProjectId,
 			},
 			Table: RAW_EXECUTION_TABLE,
@@ -68,18 +67,37 @@ func ConvertExecutions(taskCtx plugin.SubTaskContext) errors.Error {
 		Convert: func(inputRow interface{}) ([]interface{}, errors.Error) {
 			toolExecution := inputRow.(*models.ZentaoExecution)
 
-			domainBoard := &ticket.Board{
+			domainStatus := ``
+			switch toolExecution.Status {
+			case `wait`:
+				domainStatus = `FUTURE`
+			case `doing`:
+				domainStatus = `ACTIVE`
+			case `suspended`:
+				domainStatus = `SUSPENDED`
+			case `closed`:
+			case `done`:
+				domainStatus = `CLOSED`
+			}
+
+			sprint := &ticket.Sprint{
 				DomainEntity: domainlayer.DomainEntity{
-					Id: boardIdGen.Generate(toolExecution.ConnectionId, toolExecution.Id),
+					Id: executionIdGen.Generate(toolExecution.ConnectionId, toolExecution.Id),
 				},
-				Name:        toolExecution.Name,
-				Description: toolExecution.Description,
-				Url:         toolExecution.Path,
-				CreatedDate: toolExecution.OpenedDate.ToNullableTime(),
-				Type:        toolExecution.Type,
+				Name:            toolExecution.Name,
+				Url:             toolExecution.Path,
+				Status:          domainStatus,
+				StartedDate:     toolExecution.RealBegan.ToNullableTime(),
+				EndedDate:       toolExecution.RealEnd.ToNullableTime(),
+				CompletedDate:   toolExecution.ClosedDate.ToNullableTime(),
+				OriginalBoardID: projectIdGen.Generate(toolExecution.ConnectionId, toolExecution.Id),
+			}
+			boardSprint := &ticket.BoardSprint{
+				BoardId:  projectIdGen.Generate(toolExecution.ConnectionId, toolExecution.Id),
+				SprintId: sprint.Id,
 			}
 			results := make([]interface{}, 0)
-			results = append(results, domainBoard)
+			results = append(results, sprint, boardSprint)
 			return results, nil
 		},
 	})
diff --git a/backend/plugins/zentao/tasks/execution_extractor.go b/backend/plugins/zentao/tasks/execution_extractor.go
index 37c835642..f8efa2699 100644
--- a/backend/plugins/zentao/tasks/execution_extractor.go
+++ b/backend/plugins/zentao/tasks/execution_extractor.go
@@ -43,7 +43,6 @@ func ExtractExecutions(taskCtx plugin.SubTaskContext) errors.Error {
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
 				ProductId:    data.Options.ProductId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProjectId:    data.Options.ProjectId,
 			},
 			Table: RAW_EXECUTION_TABLE,
@@ -58,6 +57,7 @@ func ExtractExecutions(taskCtx plugin.SubTaskContext) errors.Error {
 				ConnectionId:   data.Options.ConnectionId,
 				Id:             res.ID,
 				Project:        res.Project,
+				ProjectId:      res.Project,
 				Model:          res.Model,
 				Type:           res.Type,
 				Lifetime:       res.Lifetime,
diff --git a/backend/plugins/zentao/tasks/product_collector.go b/backend/plugins/zentao/tasks/product_collector.go
deleted file mode 100644
index 56fbf839f..000000000
--- a/backend/plugins/zentao/tasks/product_collector.go
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-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 tasks
-
-import (
-	"encoding/json"
-	"fmt"
-	"github.com/apache/incubator-devlake/core/errors"
-	"github.com/apache/incubator-devlake/core/plugin"
-	"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
-	"io"
-	"net/http"
-	"net/url"
-)
-
-const RAW_PRODUCT_TABLE = "zentao_api_products"
-
-var _ plugin.SubTaskEntryPoint = CollectProduct
-
-func CollectProduct(taskCtx plugin.SubTaskContext) errors.Error {
-	data := taskCtx.GetData().(*ZentaoTaskData)
-	collector, err := api.NewApiCollector(api.ApiCollectorArgs{
-		RawDataSubTaskArgs: api.RawDataSubTaskArgs{
-			Ctx: taskCtx,
-			Params: ZentaoApiParams{
-				ConnectionId: data.Options.ConnectionId,
-				ExecutionId:  data.Options.ExecutionId,
-				ProductId:    data.Options.ProductId,
-				ProjectId:    data.Options.ProjectId,
-			},
-			Table: RAW_PRODUCT_TABLE,
-		},
-		ApiClient:   data.ApiClient,
-		UrlTemplate: "products/{{ .Params.ProductId }}",
-		Query: func(reqData *api.RequestData) (url.Values, errors.Error) {
-			query := url.Values{}
-			query.Set("page", fmt.Sprintf("%v", reqData.Pager.Page))
-			query.Set("limit", fmt.Sprintf("%v", reqData.Pager.Size))
-			return query, nil
-		},
-		ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) {
-			body, err := io.ReadAll(res.Body)
-			if err != nil {
-				return nil, errors.Default.Wrap(err, "error reading endpoint response by Zentao product collector")
-			}
-			res.Body.Close()
-			return []json.RawMessage{body}, nil
-		},
-	})
-	if err != nil {
-		return err
-	}
-	return collector.Execute()
-}
-
-var CollectProductMeta = plugin.SubTaskMeta{
-	Name:             "CollectProduct",
-	EntryPoint:       CollectProduct,
-	EnabledByDefault: true,
-	Description:      "Collect Product data from Zentao api",
-}
diff --git a/backend/plugins/zentao/tasks/product_convertor.go b/backend/plugins/zentao/tasks/product_convertor.go
index eeb66baa5..31f64032c 100644
--- a/backend/plugins/zentao/tasks/product_convertor.go
+++ b/backend/plugins/zentao/tasks/product_convertor.go
@@ -29,6 +29,8 @@ import (
 	"reflect"
 )
 
+const RAW_PRODUCT_TABLE = "zentao_api_products"
+
 var _ plugin.SubTaskEntryPoint = ConvertProducts
 
 var ConvertProductMeta = plugin.SubTaskMeta{
@@ -45,8 +47,7 @@ func ConvertProducts(taskCtx plugin.SubTaskContext) errors.Error {
 	boardIdGen := didgen.NewDomainIdGenerator(&models.ZentaoProduct{})
 	cursor, err := db.Cursor(
 		dal.From(&models.ZentaoProduct{}),
-		dal.Where(`_tool_zentao_products.id = ? and 
-			_tool_zentao_products.connection_id = ?`, data.Options.ProductId, data.Options.ConnectionId),
+		dal.Where(`id = ? and connection_id = ?`, data.Options.ProductId, data.Options.ConnectionId),
 	)
 	if err != nil {
 		return err
@@ -59,7 +60,6 @@ func ConvertProducts(taskCtx plugin.SubTaskContext) errors.Error {
 			Ctx: taskCtx,
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProductId:    data.Options.ProductId,
 				ProjectId:    data.Options.ProjectId,
 			},
diff --git a/backend/plugins/zentao/tasks/product_extractor.go b/backend/plugins/zentao/tasks/product_extractor.go
deleted file mode 100644
index 7cf75d2eb..000000000
--- a/backend/plugins/zentao/tasks/product_extractor.go
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
-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 tasks
-
-import (
-	"encoding/json"
-	"github.com/apache/incubator-devlake/core/errors"
-	"github.com/apache/incubator-devlake/core/plugin"
-	"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
-	"github.com/apache/incubator-devlake/plugins/zentao/models"
-)
-
-var _ plugin.SubTaskEntryPoint = ExtractProducts
-
-var ExtractProductMeta = plugin.SubTaskMeta{
-	Name:             "extractProducts",
-	EntryPoint:       ExtractProducts,
-	EnabledByDefault: true,
-	Description:      "extract Zentao products",
-	DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
-}
-
-func ExtractProducts(taskCtx plugin.SubTaskContext) errors.Error {
-	data := taskCtx.GetData().(*ZentaoTaskData)
-	extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
-		RawDataSubTaskArgs: api.RawDataSubTaskArgs{
-			Ctx: taskCtx,
-			Params: ZentaoApiParams{
-				ConnectionId: data.Options.ConnectionId,
-				ExecutionId:  data.Options.ExecutionId,
-				ProductId:    data.Options.ProductId,
-				ProjectId:    data.Options.ProjectId,
-			},
-			Table: RAW_PRODUCT_TABLE,
-		},
-		Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
-			res := &models.ZentaoProductRes{}
-			err := json.Unmarshal(row.Data, res)
-			if err != nil {
-				return nil, errors.Default.Wrap(err, "error reading endpoint response by Zentao product extractor")
-			}
-			product := &models.ZentaoProduct{
-				ConnectionId:   data.Options.ConnectionId,
-				Id:             int64(res.ID),
-				Program:        res.Program,
-				Name:           res.Name,
-				Code:           res.Code,
-				Bind:           res.Bind,
-				Line:           res.Line,
-				Type:           res.Type,
-				Status:         res.Status,
-				SubStatus:      res.SubStatus,
-				Description:    res.Description,
-				POId:           getAccountId(res.PO),
-				QDId:           getAccountId(res.QD),
-				RDId:           getAccountId(res.RD),
-				Acl:            res.Acl,
-				Reviewer:       res.Reviewer,
-				CreatedById:    getAccountId(res.CreatedBy),
-				CreatedDate:    res.CreatedDate,
-				CreatedVersion: res.CreatedVersion,
-				OrderIn:        res.OrderIn,
-				Deleted:        res.Deleted,
-				Plans:          res.Plans,
-				Releases:       res.Releases,
-				Builds:         res.Builds,
-				Cases:          res.Cases,
-				Projects:       res.Projects,
-				Executions:     res.Executions,
-				Bugs:           res.Bugs,
-				Docs:           res.Docs,
-				Progress:       res.Progress,
-				CaseReview:     res.CaseReview,
-			}
-			results := make([]interface{}, 0)
-			results = append(results, product)
-			return results, nil
-		},
-	})
-
-	if err != nil {
-		return err
-	}
-
-	return extractor.Execute()
-}
diff --git a/backend/plugins/zentao/tasks/project_collector.go b/backend/plugins/zentao/tasks/project_collector.go
deleted file mode 100644
index d5f451258..000000000
--- a/backend/plugins/zentao/tasks/project_collector.go
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
-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 tasks
-
-import (
-	"encoding/json"
-	"fmt"
-	"github.com/apache/incubator-devlake/core/errors"
-	"github.com/apache/incubator-devlake/core/plugin"
-	"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
-	"net/http"
-	"net/url"
-)
-
-const RAW_PROJECT_TABLE = "zentao_api_projects"
-
-var _ plugin.SubTaskEntryPoint = CollectProject
-
-func CollectProject(taskCtx plugin.SubTaskContext) errors.Error {
-	data := taskCtx.GetData().(*ZentaoTaskData)
-	if data.Options.ProjectId == 0 {
-		return nil
-	}
-	collector, err := api.NewApiCollector(api.ApiCollectorArgs{
-		RawDataSubTaskArgs: api.RawDataSubTaskArgs{
-			Ctx: taskCtx,
-			Params: ZentaoApiParams{
-				ConnectionId: data.Options.ConnectionId,
-				ExecutionId:  data.Options.ExecutionId,
-				ProductId:    data.Options.ProductId,
-				ProjectId:    data.Options.ProjectId,
-			},
-			Table: RAW_PROJECT_TABLE,
-		},
-		ApiClient:   data.ApiClient,
-		UrlTemplate: "projects",
-		Query: func(reqData *api.RequestData) (url.Values, errors.Error) {
-			query := url.Values{}
-			query.Set("page", fmt.Sprintf("%v", reqData.Pager.Page))
-			query.Set("limit", fmt.Sprintf("%v", reqData.Pager.Size))
-			return query, nil
-		},
-		ResponseParser: func(res *http.Response) ([]json.RawMessage, errors.Error) {
-			var data struct {
-				Projects []json.RawMessage `json:"projects"`
-			}
-			err := api.UnmarshalResponse(res, &data)
-			return data.Projects, err
-		},
-	})
-	if err != nil {
-		return err
-	}
-
-	return collector.Execute()
-}
-
-var CollectProjectMeta = plugin.SubTaskMeta{
-	Name:             "CollectProject",
-	EntryPoint:       CollectProject,
-	EnabledByDefault: true,
-	Description:      "Collect Project data from Zentao api",
-}
diff --git a/backend/plugins/zentao/tasks/product_convertor.go b/backend/plugins/zentao/tasks/project_convertor.go
similarity index 69%
copy from backend/plugins/zentao/tasks/product_convertor.go
copy to backend/plugins/zentao/tasks/project_convertor.go
index eeb66baa5..3c2d146ee 100644
--- a/backend/plugins/zentao/tasks/product_convertor.go
+++ b/backend/plugins/zentao/tasks/project_convertor.go
@@ -29,53 +29,53 @@ import (
 	"reflect"
 )
 
-var _ plugin.SubTaskEntryPoint = ConvertProducts
+const RAW_PROJECT_TABLE = "zentao_api_projects"
 
-var ConvertProductMeta = plugin.SubTaskMeta{
-	Name:             "convertProducts",
-	EntryPoint:       ConvertProducts,
+var _ plugin.SubTaskEntryPoint = ConvertProjects
+
+var ConvertProjectMeta = plugin.SubTaskMeta{
+	Name:             "convertProjects",
+	EntryPoint:       ConvertProjects,
 	EnabledByDefault: true,
-	Description:      "convert Zentao products",
+	Description:      "convert Zentao projects",
 	DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
 }
 
-func ConvertProducts(taskCtx plugin.SubTaskContext) errors.Error {
+func ConvertProjects(taskCtx plugin.SubTaskContext) errors.Error {
 	data := taskCtx.GetData().(*ZentaoTaskData)
 	db := taskCtx.GetDal()
-	boardIdGen := didgen.NewDomainIdGenerator(&models.ZentaoProduct{})
+	boardIdGen := didgen.NewDomainIdGenerator(&models.ZentaoProject{})
 	cursor, err := db.Cursor(
-		dal.From(&models.ZentaoProduct{}),
-		dal.Where(`_tool_zentao_products.id = ? and 
-			_tool_zentao_products.connection_id = ?`, data.Options.ProductId, data.Options.ConnectionId),
+		dal.From(&models.ZentaoProject{}),
+		dal.Where(`id = ? and connection_id = ?`, data.Options.ProjectId, data.Options.ConnectionId),
 	)
 	if err != nil {
 		return err
 	}
 	defer cursor.Close()
 	convertor, err := api.NewDataConverter(api.DataConverterArgs{
-		InputRowType: reflect.TypeOf(models.ZentaoProduct{}),
+		InputRowType: reflect.TypeOf(models.ZentaoProject{}),
 		Input:        cursor,
 		RawDataSubTaskArgs: api.RawDataSubTaskArgs{
 			Ctx: taskCtx,
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProductId:    data.Options.ProductId,
 				ProjectId:    data.Options.ProjectId,
 			},
-			Table: RAW_PRODUCT_TABLE,
+			Table: RAW_PROJECT_TABLE,
 		},
 		Convert: func(inputRow interface{}) ([]interface{}, errors.Error) {
-			toolProduct := inputRow.(*models.ZentaoProduct)
+			toolProject := inputRow.(*models.ZentaoProject)
 
 			domainBoard := &ticket.Board{
 				DomainEntity: domainlayer.DomainEntity{
-					Id: boardIdGen.Generate(toolProduct.ConnectionId, toolProduct.Id),
+					Id: boardIdGen.Generate(toolProject.ConnectionId, toolProject.Id),
 				},
-				Name:        toolProduct.Name,
-				Description: toolProduct.Description,
-				CreatedDate: toolProduct.CreatedDate.ToNullableTime(),
-				Type:        toolProduct.Type,
+				Name:        toolProject.Name,
+				Description: toolProject.Description,
+				CreatedDate: toolProject.OpenedDate.ToNullableTime(),
+				Type:        toolProject.Type,
 			}
 			results := make([]interface{}, 0)
 			results = append(results, domainBoard)
diff --git a/backend/plugins/zentao/tasks/project_extractor.go b/backend/plugins/zentao/tasks/project_extractor.go
deleted file mode 100644
index 319792dc7..000000000
--- a/backend/plugins/zentao/tasks/project_extractor.go
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
-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 tasks
-
-import (
-	"encoding/json"
-	"github.com/apache/incubator-devlake/core/errors"
-	"github.com/apache/incubator-devlake/core/plugin"
-	"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
-	"github.com/apache/incubator-devlake/plugins/zentao/models"
-)
-
-var _ plugin.SubTaskEntryPoint = ExtractProjects
-
-var ExtractProjectMeta = plugin.SubTaskMeta{
-	Name:             "extractProjects",
-	EntryPoint:       ExtractProjects,
-	EnabledByDefault: true,
-	Description:      "extract Zentao projects",
-	DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
-}
-
-func ExtractProjects(taskCtx plugin.SubTaskContext) errors.Error {
-	data := taskCtx.GetData().(*ZentaoTaskData)
-	extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
-		RawDataSubTaskArgs: api.RawDataSubTaskArgs{
-			Ctx: taskCtx,
-			Params: ZentaoApiParams{
-				ConnectionId: data.Options.ConnectionId,
-				ExecutionId:  data.Options.ExecutionId,
-				ProductId:    data.Options.ProductId,
-				ProjectId:    data.Options.ProjectId,
-			},
-			Table: RAW_PROJECT_TABLE,
-		},
-		Extract: func(row *api.RawData) ([]interface{}, errors.Error) {
-			project := &models.ZentaoProject{}
-			err := json.Unmarshal(row.Data, project)
-			if err != nil {
-				return nil, errors.Default.Wrap(err, "error reading endpoint response by Zentao project executor")
-			}
-			project.ConnectionId = data.Options.ConnectionId
-			results := make([]interface{}, 0)
-			results = append(results, project)
-			return results, nil
-		},
-	})
-
-	if err != nil {
-		return err
-	}
-
-	return extractor.Execute()
-}
diff --git a/backend/plugins/zentao/tasks/story_collector.go b/backend/plugins/zentao/tasks/story_collector.go
index 6c05b5250..2d9d16617 100644
--- a/backend/plugins/zentao/tasks/story_collector.go
+++ b/backend/plugins/zentao/tasks/story_collector.go
@@ -33,13 +33,15 @@ var _ plugin.SubTaskEntryPoint = CollectStory
 
 func CollectStory(taskCtx plugin.SubTaskContext) errors.Error {
 	data := taskCtx.GetData().(*ZentaoTaskData)
+	if data.Options.ProductId == 0 {
+		return nil
+	}
 	collector, err := api.NewApiCollector(api.ApiCollectorArgs{
 		RawDataSubTaskArgs: api.RawDataSubTaskArgs{
 			Ctx: taskCtx,
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
 				ProductId:    data.Options.ProductId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProjectId:    data.Options.ProjectId,
 			},
 			Table: RAW_STORY_TABLE,
@@ -78,4 +80,5 @@ var CollectStoryMeta = plugin.SubTaskMeta{
 	EntryPoint:       CollectStory,
 	EnabledByDefault: true,
 	Description:      "Collect Story data from Zentao api",
+	DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
 }
diff --git a/backend/plugins/zentao/tasks/story_convertor.go b/backend/plugins/zentao/tasks/story_convertor.go
index aadfc65c1..f80093fbe 100644
--- a/backend/plugins/zentao/tasks/story_convertor.go
+++ b/backend/plugins/zentao/tasks/story_convertor.go
@@ -47,7 +47,7 @@ func ConvertStory(taskCtx plugin.SubTaskContext) errors.Error {
 	boardIdGen := didgen.NewDomainIdGenerator(&models.ZentaoProduct{})
 	cursor, err := db.Cursor(
 		dal.From(&models.ZentaoStory{}),
-		dal.Where(`_tool_zentao_stories.product = ? and 
+		dal.Where(`_tool_zentao_stories.product = ? and
 			_tool_zentao_stories.connection_id = ?`, data.Options.ProductId, data.Options.ConnectionId),
 	)
 	if err != nil {
@@ -62,7 +62,6 @@ func ConvertStory(taskCtx plugin.SubTaskContext) errors.Error {
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
 				ProductId:    data.Options.ProductId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProjectId:    data.Options.ProjectId,
 			},
 			Table: RAW_STORY_TABLE,
diff --git a/backend/plugins/zentao/tasks/story_extractor.go b/backend/plugins/zentao/tasks/story_extractor.go
index cdc6c802f..e014701d2 100644
--- a/backend/plugins/zentao/tasks/story_extractor.go
+++ b/backend/plugins/zentao/tasks/story_extractor.go
@@ -43,7 +43,6 @@ func ExtractStory(taskCtx plugin.SubTaskContext) errors.Error {
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
 				ProductId:    data.Options.ProductId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProjectId:    data.Options.ProjectId,
 			},
 			Table: RAW_STORY_TABLE,
diff --git a/backend/plugins/zentao/tasks/task_collector.go b/backend/plugins/zentao/tasks/task_collector.go
index 9b7409b70..0547b8a9b 100644
--- a/backend/plugins/zentao/tasks/task_collector.go
+++ b/backend/plugins/zentao/tasks/task_collector.go
@@ -20,36 +20,58 @@ package tasks
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-devlake/core/dal"
 	"github.com/apache/incubator-devlake/core/errors"
 	"github.com/apache/incubator-devlake/core/plugin"
 	"github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+	"github.com/apache/incubator-devlake/plugins/zentao/models"
 	"net/http"
 	"net/url"
+	"reflect"
 )
 
 const RAW_TASK_TABLE = "zentao_api_tasks"
 
+type ExecuteInput struct {
+	Id int64
+}
+
 var _ plugin.SubTaskEntryPoint = CollectTask
 
 func CollectTask(taskCtx plugin.SubTaskContext) errors.Error {
 	data := taskCtx.GetData().(*ZentaoTaskData)
-	if data.Options.ExecutionId == 0 {
+	if data.Options.ProjectId == 0 {
 		return nil
 	}
+	cursor, err := taskCtx.GetDal().Cursor(
+		dal.Select(`id`),
+		dal.From(&models.ZentaoExecution{}),
+		dal.Where(`project_id = ? and connection_id = ?`, data.Options.ProjectId, data.Options.ConnectionId),
+	)
+	if err != nil {
+		return err
+	}
+	defer cursor.Close()
+
+	iterator, err := api.NewDalCursorIterator(taskCtx.GetDal(), cursor, reflect.TypeOf(ExecuteInput{}))
+	if err != nil {
+		return err
+	}
+
 	collector, err := api.NewApiCollector(api.ApiCollectorArgs{
 		RawDataSubTaskArgs: api.RawDataSubTaskArgs{
 			Ctx: taskCtx,
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
 				ProductId:    data.Options.ProductId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProjectId:    data.Options.ProjectId,
 			},
 			Table: RAW_TASK_TABLE,
 		},
+		Input:       iterator,
 		ApiClient:   data.ApiClient,
 		PageSize:    100,
-		UrlTemplate: "/executions/{{ .Params.ExecutionId }}/tasks",
+		UrlTemplate: "/executions/{{ .Input.Id }}/tasks",
 		Query: func(reqData *api.RequestData) (url.Values, errors.Error) {
 			query := url.Values{}
 			query.Set("page", fmt.Sprintf("%v", reqData.Pager.Page))
@@ -79,4 +101,5 @@ var CollectTaskMeta = plugin.SubTaskMeta{
 	EntryPoint:       CollectTask,
 	EnabledByDefault: true,
 	Description:      "Collect Task data from Zentao api",
+	DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
 }
diff --git a/backend/plugins/zentao/tasks/task_convertor.go b/backend/plugins/zentao/tasks/task_convertor.go
index 60e9b2ed8..c28271187 100644
--- a/backend/plugins/zentao/tasks/task_convertor.go
+++ b/backend/plugins/zentao/tasks/task_convertor.go
@@ -48,8 +48,7 @@ func ConvertTask(taskCtx plugin.SubTaskContext) errors.Error {
 	taskIdGen := didgen.NewDomainIdGenerator(&models.ZentaoTask{})
 	cursor, err := db.Cursor(
 		dal.From(&models.ZentaoTask{}),
-		dal.Where(`_tool_zentao_tasks.execution = ? and 
-			_tool_zentao_tasks.connection_id = ?`, data.Options.ExecutionId, data.Options.ConnectionId),
+		dal.Where(`project = ? and connection_id = ?`, data.Options.ProjectId, data.Options.ConnectionId),
 	)
 	if err != nil {
 		return err
@@ -63,7 +62,6 @@ func ConvertTask(taskCtx plugin.SubTaskContext) errors.Error {
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
 				ProductId:    data.Options.ProductId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProjectId:    data.Options.ProjectId,
 			},
 			Table: RAW_TASK_TABLE,
@@ -103,7 +101,7 @@ func ConvertTask(taskCtx plugin.SubTaskContext) errors.Error {
 				domainEntity.LeadTimeMinutes = int64(toolEntity.ClosedDate.ToNullableTime().Sub(toolEntity.OpenedDate.ToTime()).Minutes())
 			}
 			domainBoardIssue := &ticket.BoardIssue{
-				BoardId: boardIdGen.Generate(data.Options.ConnectionId, data.Options.ExecutionId),
+				BoardId: boardIdGen.Generate(data.Options.ConnectionId, toolEntity.Execution),
 				IssueId: domainEntity.Id,
 			}
 			results := make([]interface{}, 0)
diff --git a/backend/plugins/zentao/tasks/task_data.go b/backend/plugins/zentao/tasks/task_data.go
index 40193f672..83d47e7fe 100644
--- a/backend/plugins/zentao/tasks/task_data.go
+++ b/backend/plugins/zentao/tasks/task_data.go
@@ -19,6 +19,7 @@ package tasks
 
 import (
 	"fmt"
+	"github.com/apache/incubator-devlake/core/errors"
 	helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
 	"github.com/mitchellh/mapstructure"
 )
@@ -26,7 +27,6 @@ import (
 type ZentaoApiParams struct {
 	ConnectionId uint64
 	ProductId    int64
-	ExecutionId  int64
 	ProjectId    int64
 }
 
@@ -35,11 +35,12 @@ type ZentaoOptions struct {
 	// Such As How many rows do your want
 	// You can use it in subtasks, and you need to pass it to main.go and pipelines.
 	ConnectionId uint64 `json:"connectionId"`
-	ProductId    int64
-	ExecutionId  int64
-	ProjectId    int64
-	Tasks        []string `json:"tasks,omitempty"`
-	Since        string
+	ProductId    int64  `json:"productId" mapstructure:"productId"`
+	ProjectId    int64  `json:"projectId" mapstructure:"projectId"`
+	// TODO not support now
+	TimeAfter string `json:"timeAfter" mapstructure:"timeAfter,omitempty"`
+	//TransformationRuleId                uint64 `json:"transformationZentaoeId" mapstructure:"transformationRuleId,omitempty"`
+	//*models.ZentaoTransformationRule `mapstructure:"transformationRules,omitempty" json:"transformationRules"`
 }
 
 type ZentaoTaskData struct {
@@ -57,8 +58,17 @@ func DecodeAndValidateTaskOptions(options map[string]interface{}) (*ZentaoOption
 	if op.ConnectionId == 0 {
 		return nil, fmt.Errorf("connectionId is invalid")
 	}
-	if op.ProductId == 0 {
+	if op.ProductId == 0 && op.ProjectId == 0 {
 		return nil, fmt.Errorf("please set productId")
 	}
 	return &op, nil
 }
+
+func EncodeTaskOptions(op *ZentaoOptions) (map[string]interface{}, errors.Error) {
+	var result map[string]interface{}
+	err := helper.Decode(op, &result, nil)
+	if err != nil {
+		return nil, err
+	}
+	return result, nil
+}
diff --git a/backend/plugins/zentao/tasks/task_extractor.go b/backend/plugins/zentao/tasks/task_extractor.go
index e8c056425..46988d769 100644
--- a/backend/plugins/zentao/tasks/task_extractor.go
+++ b/backend/plugins/zentao/tasks/task_extractor.go
@@ -43,7 +43,6 @@ func ExtractTask(taskCtx plugin.SubTaskContext) errors.Error {
 			Params: ZentaoApiParams{
 				ConnectionId: data.Options.ConnectionId,
 				ProductId:    data.Options.ProductId,
-				ExecutionId:  data.Options.ExecutionId,
 				ProjectId:    data.Options.ProjectId,
 			},
 			Table: RAW_TASK_TABLE,
diff --git a/backend/plugins/zentao/zentao.go b/backend/plugins/zentao/zentao.go
index 11282bf15..fcbbeaa55 100644
--- a/backend/plugins/zentao/zentao.go
+++ b/backend/plugins/zentao/zentao.go
@@ -31,14 +31,12 @@ func main() {
 	cmd := &cobra.Command{Use: "zentao"}
 
 	connectionId := cmd.Flags().Uint64P("connectionId", "c", 0, "zentao connection id")
-	executionId := cmd.Flags().IntP("executionId", "e", 8, "execution id")
 	productId := cmd.Flags().IntP("productId", "o", 8, "product id")
 	projectId := cmd.Flags().IntP("projectId", "p", 8, "project id")
 
 	cmd.Run = func(cmd *cobra.Command, args []string) {
 		runner.DirectRun(cmd, args, PluginEntry, map[string]interface{}{
 			"connectionId": *connectionId,
-			"executionId":  *executionId,
 			"productId":    *productId,
 			"projectId":    *projectId,
 		})
diff --git a/config-ui/src/pages/blueprint/create/step-three/use-columns.tsx b/config-ui/src/pages/blueprint/create/step-three/use-columns.tsx
index b8d2a8f96..22bbff89e 100644
--- a/config-ui/src/pages/blueprint/create/step-three/use-columns.tsx
+++ b/config-ui/src/pages/blueprint/create/step-three/use-columns.tsx
@@ -58,7 +58,7 @@ export const useColumns = ({ onDetail }: Props) => {
           key: 'action',
           align: 'center',
           render: (_: any, connection: BPConnectionItemType) =>
-            connection.plugin === 'sonarqube' ? (
+            connection.plugin === 'sonarqube' || connection.plugin === 'zentao' ? (
               'No Transformation Required'
             ) : (
               <Button
diff --git a/config-ui/src/pages/blueprint/detail/panel/configuration.tsx b/config-ui/src/pages/blueprint/detail/panel/configuration.tsx
index d2b52e2a4..d7b042769 100644
--- a/config-ui/src/pages/blueprint/detail/panel/configuration.tsx
+++ b/config-ui/src/pages/blueprint/detail/panel/configuration.tsx
@@ -162,7 +162,7 @@ export const Configuration = ({ blueprint, operating, onUpdate, onRefresh }: Pro
                 <Icon icon="annotation" color={Colors.BLUE2} />
                 <span>Change Data Scope</span>
               </div>
-              {row.plugin !== 'sonarqube' && (
+              {row.plugin !== 'sonarqube' && row.plugin !== 'zentao' && (
                 <>
                   <div
                     className="item"
diff --git a/config-ui/src/pages/pipeline/components/task/index.tsx b/config-ui/src/pages/pipeline/components/task/index.tsx
index badfcb9f4..e7b4d3eda 100644
--- a/config-ui/src/pages/pipeline/components/task/index.tsx
+++ b/config-ui/src/pages/pipeline/components/task/index.tsx
@@ -66,6 +66,13 @@ export const PipelineTask = ({ task }: Props) => {
       case ['sonarqube'].includes(config.plugin):
         name = `${name}:${options.projectKey}`;
         break;
+      case ['zentao'].includes(config.plugin):
+        if (options.projectId) {
+          name = `${name}:project/${options.projectId}`;
+        } else {
+          name = `${name}:product/${options.productId}`;
+        }
+        break;
     }
 
     return [config.icon, name];
diff --git a/config-ui/src/plugins/components/data-scope/api.ts b/config-ui/src/plugins/components/data-scope/api.ts
index 20b12bbc9..dc16c2005 100644
--- a/config-ui/src/plugins/components/data-scope/api.ts
+++ b/config-ui/src/plugins/components/data-scope/api.ts
@@ -26,3 +26,9 @@ export const updateDataScope = (plugin: string, connectionId: ID, payload: any)
     method: 'put',
     data: payload,
   });
+
+export const updateDataScopeWithType = (plugin: string, connectionId: ID, type: string, payload: any) =>
+  request(`/plugins/${plugin}/connections/${connectionId}/${type}/scopes`, {
+    method: 'put',
+    data: payload,
+  });
diff --git a/config-ui/src/plugins/components/data-scope/index.tsx b/config-ui/src/plugins/components/data-scope/index.tsx
index 7f86399a9..7beed0529 100644
--- a/config-ui/src/plugins/components/data-scope/index.tsx
+++ b/config-ui/src/plugins/components/data-scope/index.tsx
@@ -31,6 +31,7 @@ import { MultiSelector } from '@/components';
 import type { UseDataScope } from './use-data-scope';
 import { useDataScope } from './use-data-scope';
 import * as S from './styled';
+import {ZentaoDataScope} from "@/plugins/register/zentao";
 
 interface Props extends UseDataScope {
   onCancel?: () => void;
@@ -70,6 +71,10 @@ export const DataScope = ({ plugin, connectionId, entities, onCancel, ...props }
         {plugin === 'sonarqube' && (
           <SonarQubeDataScope connectionId={connectionId} selectedItems={selectedScope} onChangeItems={onChangeScope} />
         )}
+
+        {plugin === 'zentao' && (
+          <ZentaoDataScope connectionId={connectionId} selectedItems={selectedScope} onChangeItems={onChangeScope} />
+        )}
       </div>
 
       <div className="block">
diff --git a/config-ui/src/plugins/components/data-scope/use-data-scope.ts b/config-ui/src/plugins/components/data-scope/use-data-scope.ts
index 95bae47b9..136f76dcf 100644
--- a/config-ui/src/plugins/components/data-scope/use-data-scope.ts
+++ b/config-ui/src/plugins/components/data-scope/use-data-scope.ts
@@ -65,6 +65,8 @@ export const useDataScope = ({ plugin, connectionId, entities, initialValues, on
         return scope.jobFullName;
       case plugin === 'bitbucket':
         return scope.bitbucketId;
+      case plugin === 'zentao':
+        return scope.type === 'project' ? `project/${scope.id}` : `product/${scope.id}`;
       case plugin === 'sonarqube':
         return scope.projectKey;
     }
@@ -85,16 +87,29 @@ export const useDataScope = ({ plugin, connectionId, entities, initialValues, on
   const handleSave = async () => {
     const scope = await Promise.all(selectedScope.map((sc: any) => getDataScope(sc)));
 
-    const [success, res] = await operator(
-      () =>
+    let request: () => Promise<any>;
+    if (plugin === 'zentao') {
+      request = async () => {
+        return [
+          ...(await API.updateDataScopeWithType(plugin, connectionId, 'product', {
+            data: scope.filter((s) => s.type !== 'project').map((sc: any) => omit(sc, 'from')),
+          })),
+          ...(await API.updateDataScopeWithType(plugin, connectionId, 'project', {
+            data: scope.filter((s) => s.type === 'project').map((sc: any) => omit(sc, 'from')),
+          })),
+        ];
+      };
+    } else {
+      request = () =>
         API.updateDataScope(plugin, connectionId, {
           data: scope.map((sc: any) => omit(sc, 'from')),
-        }),
-      {
-        setOperating: setSaving,
-        hideToast: true,
-      },
-    );
+        });
+    }
+
+    const [success, res] = await operator(request, {
+      setOperating: setSaving,
+      hideToast: true,
+    });
 
     if (success) {
       onSave?.(
diff --git a/config-ui/src/plugins/register/zentao/config.ts b/config-ui/src/plugins/register/zentao/config.ts
index 2547c53bd..4da05e9cd 100644
--- a/config-ui/src/plugins/register/zentao/config.ts
+++ b/config-ui/src/plugins/register/zentao/config.ts
@@ -25,7 +25,6 @@ export const ZenTaoConfig: PluginConfigType = {
   type: PluginType.Connection,
   plugin: 'zentao',
   name: 'ZenTao',
-  isBeta: true,
   icon: Icon,
   sort: 100,
   connection: {
diff --git a/config-ui/src/plugins/register/zentao/data-scope.tsx b/config-ui/src/plugins/register/zentao/data-scope.tsx
new file mode 100644
index 000000000..601499135
--- /dev/null
+++ b/config-ui/src/plugins/register/zentao/data-scope.tsx
@@ -0,0 +1,54 @@
+/*
+ * 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 React, { useMemo } from 'react';
+
+import { DataScopeMillerColumns } from '@/plugins';
+
+import type { ScopeItemType } from './types';
+
+interface Props {
+  connectionId: ID;
+  selectedItems: ScopeItemType[];
+  onChangeItems: (selectedItems: ScopeItemType[]) => void;
+}
+
+export const ZentaoDataScope = ({ connectionId, onChangeItems, ...props }: Props) => {
+  const selectedItems = useMemo(
+    () =>
+      props.selectedItems.map((it) => ({
+        id: it.type === 'project' ? `project/${it.id}` : `product/${it.id}`,
+        name: it.name,
+        data: it,
+      })),
+    [props.selectedItems],
+  );
+
+  return (
+    <>
+      <h3>Repositories *</h3>
+      <p>Select the repositories you would like to sync.</p>
+      <DataScopeMillerColumns
+        plugin="zentao"
+        connectionId={connectionId}
+        selectedItems={selectedItems}
+        onChangeItems={onChangeItems}
+      />
+    </>
+  );
+};
diff --git a/config-ui/src/plugins/register/zentao/index.ts b/config-ui/src/plugins/register/zentao/index.ts
index de415db39..46ed09889 100644
--- a/config-ui/src/plugins/register/zentao/index.ts
+++ b/config-ui/src/plugins/register/zentao/index.ts
@@ -17,3 +17,4 @@
  */
 
 export * from './config';
+export * from './data-scope';
diff --git a/config-ui/src/plugins/register/zentao/index.ts b/config-ui/src/plugins/register/zentao/types.ts
similarity index 81%
copy from config-ui/src/plugins/register/zentao/index.ts
copy to config-ui/src/plugins/register/zentao/types.ts
index de415db39..3b6c23b98 100644
--- a/config-ui/src/plugins/register/zentao/index.ts
+++ b/config-ui/src/plugins/register/zentao/types.ts
@@ -16,4 +16,10 @@
  *
  */
 
-export * from './config';
+export type ScopeItemType = {
+  connectionId: ID;
+  id: string;
+  name: string;
+  type: 'product/normal' | 'product/branch' | 'product/platform' | 'project';
+  // and others
+};