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

klesh 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 135802c2c Zentao update the data transformation logic (#5323)
135802c2c is described below

commit 135802c2c6e70361aae53c121796ee2789695bbf
Author: mappjzc <[email protected]>
AuthorDate: Thu Jun 1 17:03:51 2023 +0800

    Zentao update the data transformation logic (#5323)
    
    * fix: data transformation logic
    
    Update the data transformation logic
    Fix issue sprints and boards.
    Add changelog convert.
    
    Nddtfjiang <[email protected]>
    
    * feat: add translation rule
    
    Add translation rule.
    
    Nddtfjiang <[email protected]>
    
    * fix: e2e fix
    
    Fix e2e test error
    Fix issue GetStatus lost
    
    Nddtfjiang <[email protected]>
    
    * feat: add typemapping to zentao
    
    Add TypeMapping to zentao.
    Fix some swager bugs.
    Fix zentao e2e for merge.
    Change translation rule to scope config.
    
    Nddtfjiang <[email protected]>
---
 backend/core/models/domainlayer/ticket/issue.go    |  34 ++++++
 .../core/models/migrationscripts/archived/base.go  |   8 +-
 .../pluginhelper/api/scope_generic_helper.go       |  13 +-
 backend/plugins/pagerduty/api/swagger.go           |   2 +-
 backend/plugins/trello/api/scope.go                |   8 +-
 backend/plugins/zentao/api/init.go                 |  13 +-
 backend/plugins/zentao/api/scope.go                |   4 +-
 backend/plugins/zentao/api/scope_config.go         |  83 +++++++++++++
 backend/plugins/zentao/e2e/changelog_test.go       |  10 ++
 .../e2e/snapshot_tables/_tool_zentao_bugs.csv      |  10 +-
 .../_tool_zentao_changelog_detail.csv              |  56 ++++-----
 .../e2e/snapshot_tables/_tool_zentao_stories.csv   |  16 +--
 .../e2e/snapshot_tables/_tool_zentao_tasks.csv     |   8 +-
 .../zentao/e2e/snapshot_tables/boards_product.csv  |   2 +-
 .../e2e/snapshot_tables/execution_sprint.csv       |   4 +-
 .../e2e/snapshot_tables/issue_changelogs.csv       |  19 +++
 .../zentao/e2e/snapshot_tables/issues_bug.csv      |   8 +-
 .../zentao/e2e/snapshot_tables/issues_story.csv    |  14 +--
 .../zentao/e2e/snapshot_tables/issues_task.csv     |   6 +-
 backend/plugins/zentao/impl/impl.go                |  21 ++++
 .../plugins/zentao/models/archived/scope_config.go |  38 ++++++
 backend/plugins/zentao/models/bug.go               |   3 +
 .../20230519_add_init_changelog_tables.go          |   2 +-
 .../migrationscripts/20230601_add_scope_config.go  |  76 ++++++++++++
 .../zentao/models/migrationscripts/register.go     |   1 +
 backend/plugins/zentao/models/remote_db.go         |   2 +-
 backend/plugins/zentao/models/scope_config.go      |  38 ++++++
 backend/plugins/zentao/models/story.go             |   3 +
 backend/plugins/zentao/models/task.go              |   3 +
 backend/plugins/zentao/tasks/bug_convertor.go      |  40 +++----
 backend/plugins/zentao/tasks/bug_extractor.go      |  20 ++++
 .../plugins/zentao/tasks/changelog_convertor.go    | 131 +++++++++++++++++++++
 .../plugins/zentao/tasks/execution_convertor.go    |   7 +-
 backend/plugins/zentao/tasks/product_convertor.go  |   4 +
 backend/plugins/zentao/tasks/project_convertor.go  |   4 +
 backend/plugins/zentao/tasks/shared.go             |  94 ++++++++++++++-
 backend/plugins/zentao/tasks/story_convertor.go    |  47 ++++----
 backend/plugins/zentao/tasks/story_extractor.go    |  21 ++++
 backend/plugins/zentao/tasks/task_convertor.go     |  47 ++++----
 backend/plugins/zentao/tasks/task_data.go          |  68 +++++++++--
 backend/plugins/zentao/tasks/task_extractor.go     |  21 ++++
 41 files changed, 847 insertions(+), 162 deletions(-)

diff --git a/backend/core/models/domainlayer/ticket/issue.go 
b/backend/core/models/domainlayer/ticket/issue.go
index 71852b738..977ea69ed 100644
--- a/backend/core/models/domainlayer/ticket/issue.go
+++ b/backend/core/models/domainlayer/ticket/issue.go
@@ -64,8 +64,42 @@ const (
        INCIDENT    = "INCIDENT"
        TASK        = "TASK"
 
+       // status
        TODO        = "TODO"
        DONE        = "DONE"
        IN_PROGRESS = "IN_PROGRESS"
        OTHER       = "OTHER"
 )
+
+type StatusRule struct {
+       InProgress []string
+       Todo       []string
+       Done       []string
+       Other      []string
+       Default    string
+}
+
+// GetStatus compare the input with rule for return the enmu value of status
+func GetStatus(rule *StatusRule, input interface{}) string {
+       for _, inp := range rule.InProgress {
+               if inp == input {
+                       return IN_PROGRESS
+               }
+       }
+       for _, Todo := range rule.Todo {
+               if Todo == input {
+                       return TODO
+               }
+       }
+       for _, done := range rule.Done {
+               if done == input {
+                       return DONE
+               }
+       }
+       for _, Other := range rule.Other {
+               if Other == input {
+                       return OTHER
+               }
+       }
+       return rule.Default
+}
diff --git a/backend/core/models/migrationscripts/archived/base.go 
b/backend/core/models/migrationscripts/archived/base.go
index 2d37aa076..2e30cf1d1 100644
--- a/backend/core/models/migrationscripts/archived/base.go
+++ b/backend/core/models/migrationscripts/archived/base.go
@@ -18,8 +18,9 @@ limitations under the License.
 package archived
 
 import (
-       "golang.org/x/exp/constraints"
        "time"
+
+       "golang.org/x/exp/constraints"
 )
 
 type DomainEntity struct {
@@ -33,6 +34,11 @@ type Model struct {
        UpdatedAt time.Time `json:"updatedAt"`
 }
 
+type ScopeConfig struct {
+       Model
+       Entities []string `gorm:"type:json;serializer:json" json:"entities" 
mapstructure:"entities"`
+}
+
 type GenericModel[T string | constraints.Unsigned] struct {
        ID        T         `gorm:"primaryKey" json:"id"`
        CreatedAt time.Time `json:"createdAt"`
diff --git a/backend/helpers/pluginhelper/api/scope_generic_helper.go 
b/backend/helpers/pluginhelper/api/scope_generic_helper.go
index 6d094fca8..78d161ec3 100644
--- a/backend/helpers/pluginhelper/api/scope_generic_helper.go
+++ b/backend/helpers/pluginhelper/api/scope_generic_helper.go
@@ -20,6 +20,12 @@ package api
 import (
        "encoding/json"
        "fmt"
+       "reflect"
+       "strconv"
+       "strings"
+       "sync"
+       "time"
+
        "github.com/apache/incubator-devlake/core/context"
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
@@ -30,11 +36,6 @@ import (
        serviceHelper 
"github.com/apache/incubator-devlake/helpers/pluginhelper/services"
        "github.com/go-playground/validator/v10"
        "github.com/mitchellh/mapstructure"
-       "reflect"
-       "strconv"
-       "strings"
-       "sync"
-       "time"
 )
 
 var (
@@ -42,7 +43,7 @@ var (
        tablesCacheLoader = new(sync.Once)
 )
 
-type NoTransformation struct{}
+type NoScopeConfig struct{}
 
 type (
        GenericScopeApiHelper[Conn any, Scope any, Tr any] struct {
diff --git a/backend/plugins/pagerduty/api/swagger.go 
b/backend/plugins/pagerduty/api/swagger.go
index 3d8f2a554..ffd6a9d68 100644
--- a/backend/plugins/pagerduty/api/swagger.go
+++ b/backend/plugins/pagerduty/api/swagger.go
@@ -21,7 +21,7 @@ import (
        "github.com/apache/incubator-devlake/plugins/pagerduty/tasks"
 )
 
-type PagerdutyTaskOptions tasks.PagerDutyOptions
+type PagerDutyTaskOptions tasks.PagerDutyOptions
 
 // @Summary pagerduty task options for pipelines
 // @Description This is a dummy API to demonstrate the available task options 
for pagerduty pipelines
diff --git a/backend/plugins/trello/api/scope.go 
b/backend/plugins/trello/api/scope.go
index 45db6372c..56c399197 100644
--- a/backend/plugins/trello/api/scope.go
+++ b/backend/plugins/trello/api/scope.go
@@ -20,15 +20,19 @@ 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/trello/models"
 )
 
+type ScopeReq api.ScopeReq[models.TrelloBoard]
+
 // PutScope create or update trello board
 // @Summary create or update trello board
 // @Description Create or update trello board
 // @Tags plugins/trello
 // @Accept application/json
 // @Param connectionId path int false "connection ID"
-// @Param scope body req true "json"
+// @Param scope body ScopeReq true "json"
 // @Success 200  {object} []models.TrelloBoard
 // @Failure 400  {object} shared.ApiBody "Bad Request"
 // @Failure 500  {object} shared.ApiBody "Internal Error"
@@ -60,7 +64,7 @@ func UpdateScope(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, err
 // @Param connectionId path int false "connection ID"
 // @Param pageSize query int false "page size, default 50"
 // @Param page query int false "page size, default 1"
-// @Success 200  {object} []apiBoard
+// @Success 200  {object} []models.TrelloBoard
 // @Failure 400  {object} shared.ApiBody "Bad Request"
 // @Failure 500  {object} shared.ApiBody "Internal Error"
 // @Router /plugins/trello/connections/{connectionId}/scopes/ [GET]
diff --git a/backend/plugins/zentao/api/init.go 
b/backend/plugins/zentao/api/init.go
index 440ea3e56..6eca63dab 100644
--- a/backend/plugins/zentao/api/init.go
+++ b/backend/plugins/zentao/api/init.go
@@ -31,12 +31,13 @@ type MixScopes struct {
 
 var vld *validator.Validate
 var connectionHelper *api.ConnectionApiHelper
-var productScopeHelper *api.ScopeApiHelper[models.ZentaoConnection, 
models.ZentaoProduct, api.NoTransformation]
-var projectScopeHelper *api.ScopeApiHelper[models.ZentaoConnection, 
models.ZentaoProject, api.NoTransformation]
+var productScopeHelper *api.ScopeApiHelper[models.ZentaoConnection, 
models.ZentaoProduct, models.ZentaoScopeConfig]
+var projectScopeHelper *api.ScopeApiHelper[models.ZentaoConnection, 
models.ZentaoProject, models.ZentaoScopeConfig]
 
 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
+var scHelper *api.ScopeConfigHelper[models.ZentaoScopeConfig]
 
 func Init(br context.BasicRes) {
        basicRes = br
@@ -49,11 +50,11 @@ func Init(br context.BasicRes) {
                ScopeIdFieldName:  "Id",
                ScopeIdColumnName: "id",
        }
-       productScopeHelper = api.NewScopeHelper[models.ZentaoConnection, 
models.ZentaoProduct, api.NoTransformation](
+       productScopeHelper = api.NewScopeHelper[models.ZentaoConnection, 
models.ZentaoProduct, models.ZentaoScopeConfig](
                basicRes,
                vld,
                connectionHelper,
-               api.NewScopeDatabaseHelperImpl[models.ZentaoConnection, 
models.ZentaoProduct, api.NoTransformation](
+               api.NewScopeDatabaseHelperImpl[models.ZentaoConnection, 
models.ZentaoProduct, models.ZentaoScopeConfig](
                        basicRes, connectionHelper, productParams),
                productParams,
                nil,
@@ -62,11 +63,11 @@ func Init(br context.BasicRes) {
                ScopeIdFieldName:  "Project",
                ScopeIdColumnName: "project",
        }
-       projectScopeHelper = api.NewScopeHelper[models.ZentaoConnection, 
models.ZentaoProject, api.NoTransformation](
+       projectScopeHelper = api.NewScopeHelper[models.ZentaoConnection, 
models.ZentaoProject, models.ZentaoScopeConfig](
                basicRes,
                vld,
                connectionHelper,
-               api.NewScopeDatabaseHelperImpl[models.ZentaoConnection, 
models.ZentaoProject, api.NoTransformation](
+               api.NewScopeDatabaseHelperImpl[models.ZentaoConnection, 
models.ZentaoProject, models.ZentaoScopeConfig](
                        basicRes, connectionHelper, projectParams),
                projectParams,
                nil,
diff --git a/backend/plugins/zentao/api/scope.go 
b/backend/plugins/zentao/api/scope.go
index f2a03019b..980260733 100644
--- a/backend/plugins/zentao/api/scope.go
+++ b/backend/plugins/zentao/api/scope.go
@@ -26,14 +26,14 @@ import (
 
 type ProductScopeRes struct {
        models.ZentaoProduct
-       TransformationRuleName string `json:"transformationRuleName,omitempty"`
+       ScopeConfigName string `json:"scopeConfigName,omitempty"`
 }
 
 type ProductScopeReq api.ScopeReq[models.ZentaoProduct]
 
 type ProjectScopeRes struct {
        models.ZentaoProject
-       TransformationRuleName string `json:"transformationRuleName,omitempty"`
+       ScopeConfigName string `json:"scopeConfigName,omitempty"`
 }
 
 type ProjectScopeReq api.ScopeReq[models.ZentaoProject]
diff --git a/backend/plugins/zentao/api/scope_config.go 
b/backend/plugins/zentao/api/scope_config.go
new file mode 100644
index 000000000..b12ed6fc1
--- /dev/null
+++ b/backend/plugins/zentao/api/scope_config.go
@@ -0,0 +1,83 @@
+/*
+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"
+)
+
+// CreateScopeConfig create scope config for Zentao
+// @Summary create scope config for Zentao
+// @Description create scope config for Zentao
+// @Tags plugins/zentao
+// @Accept application/json
+// @Param connectionId path int true "connectionId"
+// @Param scopeConfig body models.ZentaoScopeConfig true "scope config"
+// @Success 200  {object} models.ZentaoScopeConfig
+// @Failure 400  {object} shared.ApiBody "Bad Request"
+// @Failure 500  {object} shared.ApiBody "Internal Error"
+// @Router /plugins/zentao/connections/{connectionId}/scope_configs [POST]
+func CreateScopeConfig(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
+       return scHelper.Create(input)
+}
+
+// UpdateScopeConfig update scope config for Zentao
+// @Summary update scope config for Zentao
+// @Description update scope config for Zentao
+// @Tags plugins/zentao
+// @Accept application/json
+// @Param id path int true "id"
+// @Param connectionId path int true "connectionId"
+// @Param scopeConfig body models.ZentaoScopeConfig true "scope config"
+// @Success 200  {object} models.ZentaoScopeConfig
+// @Failure 400  {object} shared.ApiBody "Bad Request"
+// @Failure 500  {object} shared.ApiBody "Internal Error"
+// @Router /plugins/zentao/connections/{connectionId}/scope_configs/{id} 
[PATCH]
+func UpdateScopeConfig(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
+       return scHelper.Update(input)
+}
+
+// GetScopeConfig return one scope config
+// @Summary return one scope config
+// @Description return one scope config
+// @Tags plugins/zentao
+// @Param id path int true "id"
+// @Param connectionId path int true "connectionId"
+// @Success 200  {object} models.ZentaoScopeConfig
+// @Failure 400  {object} shared.ApiBody "Bad Request"
+// @Failure 500  {object} shared.ApiBody "Internal Error"
+// @Router /plugins/zentao/connections/{connectionId}/scope_configs/{id} [GET]
+func GetScopeConfig(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
+       return scHelper.Get(input)
+}
+
+// GetScopeConfigList return all scope configs
+// @Summary return all scope configs
+// @Description return all scope configs
+// @Tags plugins/zentao
+// @Param connectionId path int true "connectionId"
+// @Param pageSize query int false "page size, default 50"
+// @Param page query int false "page size, default 1"
+// @Success 200  {object} []models.ZentaoScopeConfig
+// @Failure 400  {object} shared.ApiBody "Bad Request"
+// @Failure 500  {object} shared.ApiBody "Internal Error"
+// @Router /plugins/zentao/connections/{connectionId}/scope_configs [GET]
+func GetScopeConfigList(input *plugin.ApiResourceInput) 
(*plugin.ApiResourceOutput, errors.Error) {
+       return scHelper.List(input)
+}
diff --git a/backend/plugins/zentao/e2e/changelog_test.go 
b/backend/plugins/zentao/e2e/changelog_test.go
index aed72b615..0061d5507 100644
--- a/backend/plugins/zentao/e2e/changelog_test.go
+++ b/backend/plugins/zentao/e2e/changelog_test.go
@@ -22,6 +22,7 @@ import (
 
        "github.com/apache/incubator-devlake/core/config"
        "github.com/apache/incubator-devlake/core/models/common"
+       "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
        "github.com/apache/incubator-devlake/core/runner"
        "github.com/apache/incubator-devlake/helpers/e2ehelper"
        "github.com/apache/incubator-devlake/impls/dalgorm"
@@ -92,4 +93,13 @@ func TestZentaoDbGetDataFlow(t *testing.T) {
                        CSVRelPath:  
"./snapshot_tables/_tool_zentao_changelog_detail.csv",
                        IgnoreTypes: []interface{}{common.NoPKModel{}},
                })
+
+       dataflowTester.FlushTabler(&ticket.IssueChangelogs{})
+       dataflowTester.Subtask(tasks.ConvertChangelogMeta, taskData)
+       dataflowTester.VerifyTableWithOptions(
+               &ticket.IssueChangelogs{},
+               e2ehelper.TableOptions{
+                       CSVRelPath:  "./snapshot_tables/issue_changelogs.csv",
+                       IgnoreTypes: []interface{}{common.NoPKModel{}},
+               })
 }
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bugs.csv 
b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bugs.csv
index 1f49d1a4c..5a5dd4498 100644
--- a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bugs.csv
+++ b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bugs.csv
@@ -1,13 +1,13 @@
-connection_id,id,project,product,injection,identify,branch,module,execution,plan,story,story_version,task,to_task,to_story,title,keywords,severity,pri,type,os,browser,hardware,found,steps,status,sub_status,color,confirmed,activated_count,activated_date,feedback_by,notify_email,opened_by_id,opened_by_name,opened_date,opened_build,assigned_to_id,assigned_to_name,assigned_date,deadline,resolved_by_id,resolution,resolved_build,resolved_date,closed_by_id,closed_date,duplicate_bug,link_bug,fee
 [...]
+connection_id,id,project,product,injection,identify,branch,module,execution,plan,story,story_version,task,to_task,to_story,title,keywords,severity,pri,type,os,browser,hardware,found,steps,status,sub_status,color,confirmed,activated_count,activated_date,feedback_by,notify_email,opened_by_id,opened_by_name,opened_date,opened_build,assigned_to_id,assigned_to_name,assigned_date,deadline,resolved_by_id,resolution,resolved_build,resolved_date,closed_by_id,closed_date,duplicate_bug,link_bug,fee
 [...]
 1,1,7,3,0,0,0,8,1,0,1,1,1,0,0,首页页面问题,,3,1,codeerror,,,,,"<p>[步骤]进入首页</p>
 <p>[结果]出现乱码&nbsp;&nbsp;&nbsp;&nbsp;</p>
-<p>[期望]正常显示</p>",active,,,0,0,,,,7,测试甲,2012-06-05T02:56:11.000+00:00,主干,4,开发甲,2012-06-05T02:56:11.000+00:00,,0,,,,0,,0,,0,0,0,0,,,,,,,0,0,2021-04-28T03:09:08.000+00:00,0,1,3,0,激活,normal
+<p>[期望]正常显示</p>",active,,,0,0,,,,7,测试甲,2012-06-05T02:56:11.000+00:00,主干,4,开发甲,2012-06-05T02:56:11.000+00:00,,0,,,,0,,0,,0,0,0,0,,,,,,,0,0,2021-04-28T03:09:08.000+00:00,0,1,3,0,激活,normal,http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,IN_PROGRESS,BUG,
 
1,2,7,3,0,0,0,9,1,1,2,1,15,0,0,新闻中心页面问题,hh,3,2,codeerror,",windows",",chrome",,,"<p>[步骤]进入新闻中心</p>
 <p>[结果]页面出现乱码</p>
-<p>[期望]正常显示rew</p>",delay,,,1,1,2022-10-05T04:16:44.000+00:00,,[email protected],7,测试甲,2012-06-05T02:57:11.000+00:00,主干,0,,2022-10-05T04:19:22.000+00:00,2022-10-06,0,,,,0,,0,,0,0,0,0,,,,,,,1,1,2022-10-05T04:19:22.000+00:00,0,2,3,0,过期Bug,normal
+<p>[期望]正常显示rew</p>",delay,,,1,1,2022-10-05T04:16:44.000+00:00,,[email protected],7,测试甲,2012-06-05T02:57:11.000+00:00,主干,0,,2022-10-05T04:19:22.000+00:00,2022-10-06,0,,,,0,,0,,0,0,0,0,,,,,,,1,1,2022-10-05T04:19:22.000+00:00,0,2,3,0,过期Bug,normal,http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,IN_PROGRESS,BUG,
 
1,3,7,3,0,0,0,10,1,0,3,2,6,0,0,成果展示页面问题,,3,1,codeerror,,,,,"<p>[步骤]进入成果展示&nbsp;&nbsp;&nbsp;&nbsp;</p>
 <p>[结果]乱码</p>
-<p>[期望]正常显示</p>",active,,,0,0,,,,8,测试乙,2012-06-05T02:58:22.000+00:00,主干,4,开发甲,2012-06-05T02:58:22.000+00:00,,0,,,,0,,0,,0,0,0,0,,,,,,,0,0,2021-04-28T03:09:08.000+00:00,0,1,3,0,激活,normal
+<p>[期望]正常显示</p>",active,,,0,0,,,,8,测试乙,2012-06-05T02:58:22.000+00:00,主干,4,开发甲,2012-06-05T02:58:22.000+00:00,,0,,,,0,,0,,0,0,0,0,,,,,,,0,0,2021-04-28T03:09:08.000+00:00,0,1,3,0,激活,normal,http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,IN_PROGRESS,BUG,
 1,4,7,3,0,0,0,11,1,0,4,1,9,0,0,售后服务页面问题,,3,1,codeerror,,,,,"<p>[步骤]进入售后服务</p>
 <p>[结果]乱码</p>
-<p>[期望]正常显示</p>",resolved,,,1,0,,,,9,测试丙,2012-06-05T03:00:19.000+00:00,主干,9,测试丙,2022-10-05T04:10:08.000+00:00,,1,fixed,主干,2022-10-05T04:09:59.000+00:00,0,,0,,0,0,0,0,,,,,,,0,1,2022-10-05T04:10:08.000+00:00,0,1,3,0,已解决,normal
+<p>[期望]正常显示</p>",resolved,,,1,0,,,,9,测试丙,2012-06-05T03:00:19.000+00:00,主干,9,测试丙,2022-10-05T04:10:08.000+00:00,,1,fixed,主干,2022-10-05T04:09:59.000+00:00,0,,0,,0,0,0,0,,,,,,,0,1,2022-10-05T04:10:08.000+00:00,0,1,3,0,已解决,normal,http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,DONE,BUG,
diff --git 
a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_changelog_detail.csv 
b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_changelog_detail.csv
index 88aee2bc1..ab5020347 100644
--- 
a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_changelog_detail.csv
+++ 
b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_changelog_detail.csv
@@ -1,29 +1,29 @@
 connection_id,id,changelog_id,field,old,new,diff
-1,68,68,begin,2012-06-05,2020-06-05,
-1,69,69,end,2012-12-04,2021-12-04,
-1,70,70,days,184,391,
-1,71,71,status,done,wait,
-1,72,72,begin,2013-06-05,2021-06-05,
-1,73,73,end,2014-06-04,2022-06-04,
-1,74,74,days,365,260,
-1,75,75,type,interface,codeerror,
-1,76,76,pri,0,1,
-1,77,77,pri,0,2,
-1,78,78,pri,0,1,
-1,79,79,pri,0,1,
-1,80,80,build,trunk,1,
-1,81,81,begin,2012-06-05,2020-06-05,
-1,82,82,end,2013-06-21,2021-06-21,
-1,83,83,begin,2000-01-01,2020-06-05,
-1,84,84,end,2015-01-01,2021-06-04,
-1,85,85,status,wait,doing,
-1,91,91,consumed,0,8,
-1,92,92,realStarted,0000-00-00 00:00:00,2021-04-28 13:17:54,
-1,93,93,finishedDate,,2021-04-01 16:00,
-1,94,94,status,wait,done,
-1,95,95,left,7,0,
-1,96,96,finishedBy,,dev1,
-1,97,97,realStarted,0000-00-00 00:00:00,2021-04-01 16:00,
-1,98,98,status,wait,doing,
-1,99,99,realStarted,0000-00-00 00:00:00,2021-04-01 08:00,
-1,100,100,status,wait,doing,
+1,68,112,begin,2012-06-05,2020-06-05,
+1,69,112,end,2012-12-04,2021-12-04,
+1,70,112,days,184,391,
+1,71,112,status,done,wait,
+1,72,113,begin,2013-06-05,2021-06-05,
+1,73,113,end,2014-06-04,2022-06-04,
+1,74,113,days,365,260,
+1,75,114,type,interface,codeerror,
+1,76,114,pri,0,1,
+1,77,115,pri,0,2,
+1,78,116,pri,0,1,
+1,79,117,pri,0,1,
+1,80,118,build,trunk,1,
+1,81,118,begin,2012-06-05,2020-06-05,
+1,82,118,end,2013-06-21,2021-06-21,
+1,83,119,begin,2000-01-01,2020-06-05,
+1,84,119,end,2015-01-01,2021-06-04,
+1,85,120,status,wait,doing,
+1,91,165,consumed,0,8,
+1,92,165,realStarted,0000-00-00 00:00:00,2021-04-28 13:17:54,
+1,93,165,finishedDate,,2021-04-01 16:00,
+1,94,165,status,wait,done,
+1,95,165,left,7,0,
+1,96,165,finishedBy,,dev1,
+1,97,166,realStarted,0000-00-00 00:00:00,2021-04-01 16:00,
+1,98,166,status,wait,doing,
+1,99,169,realStarted,0000-00-00 00:00:00,2021-04-01 08:00,
+1,100,169,status,wait,doing,
diff --git 
a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_stories.csv 
b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_stories.csv
index 2ece4f19a..ccce179fc 100644
--- a/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_stories.csv
+++ b/backend/plugins/zentao/e2e/snapshot_tables/_tool_zentao_stories.csv
@@ -1,8 +1,8 @@
-connection_id,id,product,branch,version,order_in,vision,parent,module,plan,source,source_note,from_bug,feedback,title,keywords,type,category,pri,estimate,status,sub_status,color,stage,lib,from_story,from_version,opened_by_id,opened_by_name,opened_date,assigned_to_id,assigned_to_name,assigned_date,approved_date,last_edited_id,last_edited_date,changed_date,reviewed_by_id,reviewed_date,closed_id,closed_date,closed_reason,activated_date,to_bug,child_stories,link_stories,link_requirements,dup
 [...]
-1,1,3,0,1,0,rnd,0,1,1,po,,0,0,首页设计和开发,,story,feature,1,1,active,,,developing,0,0,1,2,产品经理,2012-06-05T02:09:49.000+00:00,2,产品经理,,,2,2012-06-05T02:25:19.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本
 
-1,2,3,0,1,0,rnd,0,2,1,po,,0,0,新闻中心的设计和开发。,,story,feature,1,1,active,,,projected,0,0,1,2,产品经理,2012-06-05T02:16:37.000+00:00,2,产品经理,2012-06-05T02:16:37.000+00:00,,2,2012-06-05T02:25:33.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本
 
-1,3,3,0,2,0,rnd,0,3,1,po,,0,0,成果展示的设计和开发,,story,feature,1,0,active,,,developing,0,0,1,2,产品经理,2012-06-05T02:18:10.000+00:00,2,产品经理,2012-06-05T02:18:10.000+00:00,,2,2012-06-05T02:25:38.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本
 
-1,4,3,0,1,0,rnd,0,4,1,po,,0,0,售后服务的设计和开发,,story,feature,1,1,active,,,developed,0,0,1,2,产品经理,2012-06-05T02:20:16.000+00:00,2,产品经理,2012-06-05T02:20:16.000+00:00,,2,2012-06-05T02:25:42.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本
 
-1,5,3,0,1,0,rnd,0,5,1,po,,0,0,诚聘英才的设计和开发,,story,feature,1,1,reviewing,,,planned,0,0,1,2,产品经理,2012-06-05T02:21:39.000+00:00,2,产品经理,2012-06-05T02:21:39.000+00:00,,0,,,0,,0,,,,0,,,,0,0,,,0,0,1,1.0版本
 
-1,6,3,0,1,0,rnd,0,6,1,po,,0,0,合作洽谈的设计和开发,,story,feature,1,1,reviewing,,,planned,0,0,1,2,产品经理,2012-06-05T02:23:11.000+00:00,2,产品经理,2012-06-05T02:23:11.000+00:00,,0,,,0,,0,,,,0,,,,0,0,,,0,0,1,1.0版本
 
-1,7,3,0,1,0,rnd,0,7,1,po,,0,0,关于我们的设计和开发,,story,feature,1,1,reviewing,,,planned,0,0,1,2,产品经理,2012-06-05T02:24:19.000+00:00,2,产品经理,2012-06-05T02:24:19.000+00:00,,0,,,0,,0,,,,0,,,,0,0,,,0,0,1,1.0版本
 
+connection_id,id,product,branch,version,order_in,vision,parent,module,plan,source,source_note,from_bug,feedback,title,keywords,type,category,pri,estimate,status,sub_status,color,stage,lib,from_story,from_version,opened_by_id,opened_by_name,opened_date,assigned_to_id,assigned_to_name,assigned_date,approved_date,last_edited_id,last_edited_date,changed_date,reviewed_by_id,reviewed_date,closed_id,closed_date,closed_reason,activated_date,to_bug,child_stories,link_stories,link_requirements,dup
 [...]
+1,1,3,0,1,0,rnd,0,1,1,po,,0,0,首页设计和开发,,story,feature,1,1,active,,,developing,0,0,1,2,产品经理,2012-06-05T02:09:49.000+00:00,2,产品经理,,,2,2012-06-05T02:25:19.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本
 
,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,IN_PROGRESS,REQUIREMENT,
+1,2,3,0,1,0,rnd,0,2,1,po,,0,0,新闻中心的设计和开发。,,story,feature,1,1,active,,,projected,0,0,1,2,产品经理,2012-06-05T02:16:37.000+00:00,2,产品经理,2012-06-05T02:16:37.000+00:00,,2,2012-06-05T02:25:33.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本
 
,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,IN_PROGRESS,REQUIREMENT,
+1,3,3,0,2,0,rnd,0,3,1,po,,0,0,成果展示的设计和开发,,story,feature,1,0,active,,,developing,0,0,1,2,产品经理,2012-06-05T02:18:10.000+00:00,2,产品经理,2012-06-05T02:18:10.000+00:00,,2,2012-06-05T02:25:38.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本
 
,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,IN_PROGRESS,REQUIREMENT,
+1,4,3,0,1,0,rnd,0,4,1,po,,0,0,售后服务的设计和开发,,story,feature,1,1,active,,,developed,0,0,1,2,产品经理,2012-06-05T02:20:16.000+00:00,2,产品经理,2012-06-05T02:20:16.000+00:00,,2,2012-06-05T02:25:42.000+00:00,,0,2012-06-04T16:00:00.000+00:00,0,,,,0,,,,0,0,,,0,0,1,1.0版本
 
,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,IN_PROGRESS,REQUIREMENT,
+1,5,3,0,1,0,rnd,0,5,1,po,,0,0,诚聘英才的设计和开发,,story,feature,1,1,reviewing,,,planned,0,0,1,2,产品经理,2012-06-05T02:21:39.000+00:00,2,产品经理,2012-06-05T02:21:39.000+00:00,,0,,,0,,0,,,,0,,,,0,0,,,0,0,1,1.0版本
 
,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,IN_PROGRESS,REQUIREMENT,
+1,6,3,0,1,0,rnd,0,6,1,po,,0,0,合作洽谈的设计和开发,,story,feature,1,1,reviewing,,,planned,0,0,1,2,产品经理,2012-06-05T02:23:11.000+00:00,2,产品经理,2012-06-05T02:23:11.000+00:00,,0,,,0,,0,,,,0,,,,0,0,,,0,0,1,1.0版本
 
,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,IN_PROGRESS,REQUIREMENT,
+1,7,3,0,1,0,rnd,0,7,1,po,,0,0,关于我们的设计和开发,,story,feature,1,1,reviewing,,,planned,0,0,1,2,产品经理,2012-06-05T02:24:19.000+00:00,2,产品经理,2012-06-05T02:24:19.000+00:00,,0,,,0,,0,,,,0,,,,0,0,,,0,0,1,1.0版本
 
,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,IN_PROGRESS,REQUIREMENT,
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 40337d85f..a5c08897c 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,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
+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,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,http://iwater.red:8000/api.php/v1/executions/9/tasks?limit=100&page=1,TODO,TASK,
+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,http://iwater.red:8000/api.php/v1/executions/9/tasks?limit=100&page=1,TODO,TASK,
+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,http://iwater.red:8000/api.php/v1/executions/9/tasks?limit=100&page=1,TODO,TASK,
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/boards_product.csv 
b/backend/plugins/zentao/e2e/snapshot_tables/boards_product.csv
index 11a53ea68..0c3d53b5d 100644
--- a/backend/plugins/zentao/e2e/snapshot_tables/boards_product.csv
+++ b/backend/plugins/zentao/e2e/snapshot_tables/boards_product.csv
@@ -1,2 +1,2 @@
 id,name,description,url,created_date,type
-zentao:ZentaoProduct:1:3,产品名称1,"<span 
style=""background-color:#FFFFFF;"">产品描述1</span>",,2022-11-17T06:42:25.000+00:00,product/normal
+zentao:ZentaoProduct:1:3,产品名称1,"<span 
style=""background-color:#FFFFFF;"">产品描述1</span>",/product-index-3.html,2022-11-17T06:42:25.000+00:00,product/normal
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/execution_sprint.csv 
b/backend/plugins/zentao/e2e/snapshot_tables/execution_sprint.csv
index 81e23be75..13a04dcab 100644
--- a/backend/plugins/zentao/e2e/snapshot_tables/execution_sprint.csv
+++ b/backend/plugins/zentao/e2e/snapshot_tables/execution_sprint.csv
@@ -1,3 +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
+zentao:ZentaoExecution:1:1,企业网站第一期,",7,1,",ACTIVE,,,2022-06-01T00:00:00.000+00:00,zentao:ZentaoProject:1:1
+zentao:ZentaoExecution:1:12,TR5,",1091,12,",CLOSED,2022-07-07T00:00:00.000+00:00,,2022-11-03T00:00:00.000+00:00,zentao:ZentaoProject:1:1
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/issue_changelogs.csv 
b/backend/plugins/zentao/e2e/snapshot_tables/issue_changelogs.csv
new file mode 100644
index 000000000..6779fab6a
--- /dev/null
+++ b/backend/plugins/zentao/e2e/snapshot_tables/issue_changelogs.csv
@@ -0,0 +1,19 @@
+id,issue_id,author_id,author_name,field_id,field_name,original_from_value,original_to_value,from_value,to_value,created_date
+zentao:ZentaoChangelogDetail:1:112:68,1,0,admin,begin,begin,2012-06-05,2020-06-05,2012-06-05,2020-06-05,2021-04-28T11:06:58.000+00:00
+zentao:ZentaoChangelogDetail:1:112:69,1,0,admin,end,end,2012-12-04,2021-12-04,2012-12-04,2021-12-04,2021-04-28T11:06:58.000+00:00
+zentao:ZentaoChangelogDetail:1:112:70,1,0,admin,days,days,184,391,184,391,2021-04-28T11:06:58.000+00:00
+zentao:ZentaoChangelogDetail:1:112:71,1,0,admin,status,status,done,wait,done,wait,2021-04-28T11:06:58.000+00:00
+zentao:ZentaoChangelogDetail:1:113:72,2,0,admin,begin,begin,2013-06-05,2021-06-05,2013-06-05,2021-06-05,2021-04-28T11:08:02.000+00:00
+zentao:ZentaoChangelogDetail:1:113:73,2,0,admin,end,end,2014-06-04,2022-06-04,2014-06-04,2022-06-04,2021-04-28T11:08:02.000+00:00
+zentao:ZentaoChangelogDetail:1:113:74,2,0,admin,days,days,365,260,365,260,2021-04-28T11:08:02.000+00:00
+zentao:ZentaoChangelogDetail:1:114:75,1,0,admin,type,type,interface,codeerror,interface,codeerror,2021-04-28T11:09:08.000+00:00
+zentao:ZentaoChangelogDetail:1:114:76,1,0,admin,pri,pri,0,1,0,1,2021-04-28T11:09:08.000+00:00
+zentao:ZentaoChangelogDetail:1:115:77,2,0,admin,pri,pri,0,2,0,2,2021-04-28T11:09:08.000+00:00
+zentao:ZentaoChangelogDetail:1:116:78,3,0,admin,pri,pri,0,1,0,1,2021-04-28T11:09:08.000+00:00
+zentao:ZentaoChangelogDetail:1:117:79,4,0,admin,pri,pri,0,1,0,1,2021-04-28T11:09:08.000+00:00
+zentao:ZentaoChangelogDetail:1:118:80,1,0,admin,build,build,trunk,1,trunk,1,2021-04-28T11:10:06.000+00:00
+zentao:ZentaoChangelogDetail:1:118:81,1,0,admin,begin,begin,2012-06-05,2020-06-05,2012-06-05,2020-06-05,2021-04-28T11:10:06.000+00:00
+zentao:ZentaoChangelogDetail:1:118:82,1,0,admin,end,end,2013-06-21,2021-06-21,2013-06-21,2021-06-21,2021-04-28T11:10:06.000+00:00
+zentao:ZentaoChangelogDetail:1:119:83,1,0,admin,begin,begin,2000-01-01,2020-06-05,2000-01-01,2020-06-05,2021-04-28T11:11:10.000+00:00
+zentao:ZentaoChangelogDetail:1:119:84,1,0,admin,end,end,2015-01-01,2021-06-04,2015-01-01,2021-06-04,2021-04-28T11:11:10.000+00:00
+zentao:ZentaoChangelogDetail:1:120:85,1,0,admin,status,status,wait,doing,wait,doing,2021-04-28T11:12:22.000+00:00
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/issues_bug.csv 
b/backend/plugins/zentao/e2e/snapshot_tables/issues_bug.csv
index 4c2746ff0..af603b91d 100644
--- a/backend/plugins/zentao/e2e/snapshot_tables/issues_bug.csv
+++ b/backend/plugins/zentao/e2e/snapshot_tables/issues_bug.csv
@@ -1,5 +1,5 @@
 
id,url,icon_url,issue_key,title,description,epic_key,type,original_type,status,original_status,story_point,resolution_date,created_date,updated_date,lead_time_minutes,parent_issue_id,priority,original_estimate_minutes,time_spent_minutes,time_remaining_minutes,creator_id,creator_name,assignee_id,assignee_name,severity,component
-zentao:ZentaoBug:1:1,,,1,首页页面问题,,,BUG,codeerror,IN_PROGRESS,active,0,,2012-06-05T02:56:11.000+00:00,2021-04-28T03:09:08.000+00:00,0,zentao:ZentaoStory:1:1,,0,0,0,7,测试甲,4,开发甲,,
-zentao:ZentaoBug:1:2,,,2,新闻中心页面问题,,,BUG,codeerror,IN_PROGRESS,delay,0,,2012-06-05T02:57:11.000+00:00,2022-10-05T04:19:22.000+00:00,0,zentao:ZentaoStory:1:2,,0,0,0,7,测试甲,0,,,
-zentao:ZentaoBug:1:3,,,3,成果展示页面问题,,,BUG,codeerror,IN_PROGRESS,active,0,,2012-06-05T02:58:22.000+00:00,2021-04-28T03:09:08.000+00:00,0,zentao:ZentaoStory:1:3,,0,0,0,8,测试乙,4,开发甲,,
-zentao:ZentaoBug:1:4,,,4,售后服务页面问题,,,BUG,codeerror,DONE,resolved,0,,2012-06-05T03:00:19.000+00:00,2022-10-05T04:10:08.000+00:00,0,zentao:ZentaoStory:1:4,,0,0,0,9,测试丙,9,测试丙,,
+zentao:ZentaoBug:1:1,http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,,1,首页页面问题,,,BUG,codeerror,IN_PROGRESS,active,0,,2012-06-05T02:56:11.000+00:00,2021-04-28T03:09:08.000+00:00,0,zentao:ZentaoStory:1:1,VeryHigh,0,0,0,7,测试甲,4,开发甲,,
+zentao:ZentaoBug:1:2,http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,,2,新闻中心页面问题,,,BUG,codeerror,IN_PROGRESS,delay,0,,2012-06-05T02:57:11.000+00:00,2022-10-05T04:19:22.000+00:00,0,zentao:ZentaoStory:1:2,High,0,0,0,7,测试甲,0,,,
+zentao:ZentaoBug:1:3,http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,,3,成果展示页面问题,,,BUG,codeerror,IN_PROGRESS,active,0,,2012-06-05T02:58:22.000+00:00,2021-04-28T03:09:08.000+00:00,0,zentao:ZentaoStory:1:3,VeryHigh,0,0,0,8,测试乙,4,开发甲,,
+zentao:ZentaoBug:1:4,http://iwater.red:8000/api.php/v1/products/1/bugs?limit=100&page=1,,4,售后服务页面问题,,,BUG,codeerror,DONE,resolved,0,,2012-06-05T03:00:19.000+00:00,2022-10-05T04:10:08.000+00:00,0,zentao:ZentaoStory:1:4,VeryHigh,0,0,0,9,测试丙,9,测试丙,,
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/issues_story.csv 
b/backend/plugins/zentao/e2e/snapshot_tables/issues_story.csv
index 6e057c7f3..353971be5 100644
--- a/backend/plugins/zentao/e2e/snapshot_tables/issues_story.csv
+++ b/backend/plugins/zentao/e2e/snapshot_tables/issues_story.csv
@@ -1,8 +1,8 @@
 
id,url,icon_url,issue_key,title,description,epic_key,type,original_type,status,original_status,story_point,resolution_date,created_date,updated_date,lead_time_minutes,parent_issue_id,priority,original_estimate_minutes,time_spent_minutes,time_remaining_minutes,creator_id,creator_name,assignee_id,assignee_name,severity,component
-zentao:ZentaoStory:1:1,,,1,首页设计和开发,,,REQUIREMENT,story,IN_PROGRESS,developing,0,,2012-06-05T02:09:49.000+00:00,2012-06-05T02:25:19.000+00:00,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,
-zentao:ZentaoStory:1:2,,,2,新闻中心的设计和开发。,,,REQUIREMENT,story,IN_PROGRESS,projected,0,,2012-06-05T02:16:37.000+00:00,2012-06-05T02:25:33.000+00:00,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,
-zentao:ZentaoStory:1:3,,,3,成果展示的设计和开发,,,REQUIREMENT,story,IN_PROGRESS,developing,0,,2012-06-05T02:18:10.000+00:00,2012-06-05T02:25:38.000+00:00,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,
-zentao:ZentaoStory:1:4,,,4,售后服务的设计和开发,,,REQUIREMENT,story,IN_PROGRESS,developed,0,,2012-06-05T02:20:16.000+00:00,2012-06-05T02:25:42.000+00:00,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,
-zentao:ZentaoStory:1:5,,,5,诚聘英才的设计和开发,,,REQUIREMENT,story,IN_PROGRESS,planned,0,,2012-06-05T02:21:39.000+00:00,,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,
-zentao:ZentaoStory:1:6,,,6,合作洽谈的设计和开发,,,REQUIREMENT,story,IN_PROGRESS,planned,0,,2012-06-05T02:23:11.000+00:00,,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,
-zentao:ZentaoStory:1:7,,,7,关于我们的设计和开发,,,REQUIREMENT,story,IN_PROGRESS,planned,0,,2012-06-05T02:24:19.000+00:00,,0,zentao:ZentaoStory:1:0,,0,0,0,2,产品经理,2,产品经理,,
+zentao:ZentaoStory:1:1,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,,1,首页设计和开发,,,REQUIREMENT,story.feature,IN_PROGRESS,active-developing,0,,2012-06-05T02:09:49.000+00:00,2012-06-05T02:25:19.000+00:00,0,zentao:ZentaoStory:1:0,VeryHigh,0,0,0,2,产品经理,2,产品经理,,
+zentao:ZentaoStory:1:2,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,,2,新闻中心的设计和开发。,,,REQUIREMENT,story.feature,IN_PROGRESS,active-projected,0,,2012-06-05T02:16:37.000+00:00,2012-06-05T02:25:33.000+00:00,0,zentao:ZentaoStory:1:0,VeryHigh,0,0,0,2,产品经理,2,产品经理,,
+zentao:ZentaoStory:1:3,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,,3,成果展示的设计和开发,,,REQUIREMENT,story.feature,IN_PROGRESS,active-developing,0,,2012-06-05T02:18:10.000+00:00,2012-06-05T02:25:38.000+00:00,0,zentao:ZentaoStory:1:0,VeryHigh,0,0,0,2,产品经理,2,产品经理,,
+zentao:ZentaoStory:1:4,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,,4,售后服务的设计和开发,,,REQUIREMENT,story.feature,IN_PROGRESS,active-developed,0,,2012-06-05T02:20:16.000+00:00,2012-06-05T02:25:42.000+00:00,0,zentao:ZentaoStory:1:0,VeryHigh,0,0,0,2,产品经理,2,产品经理,,
+zentao:ZentaoStory:1:5,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,,5,诚聘英才的设计和开发,,,REQUIREMENT,story.feature,IN_PROGRESS,reviewing-planned,0,,2012-06-05T02:21:39.000+00:00,,0,zentao:ZentaoStory:1:0,VeryHigh,0,0,0,2,产品经理,2,产品经理,,
+zentao:ZentaoStory:1:6,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,,6,合作洽谈的设计和开发,,,REQUIREMENT,story.feature,IN_PROGRESS,reviewing-planned,0,,2012-06-05T02:23:11.000+00:00,,0,zentao:ZentaoStory:1:0,VeryHigh,0,0,0,2,产品经理,2,产品经理,,
+zentao:ZentaoStory:1:7,http://iwater.red:8000/api.php/v1/products/1/stories?limit=100&page=1,,7,关于我们的设计和开发,,,REQUIREMENT,story.feature,IN_PROGRESS,reviewing-planned,0,,2012-06-05T02:24:19.000+00:00,,0,zentao:ZentaoStory:1:0,VeryHigh,0,0,0,2,产品经理,2,产品经理,,
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/issues_task.csv 
b/backend/plugins/zentao/e2e/snapshot_tables/issues_task.csv
index e54e48525..de0e33baa 100644
--- a/backend/plugins/zentao/e2e/snapshot_tables/issues_task.csv
+++ b/backend/plugins/zentao/e2e/snapshot_tables/issues_task.csv
@@ -1,4 +1,4 @@
 
id,url,icon_url,issue_key,title,description,epic_key,type,original_type,status,original_status,story_point,resolution_date,created_date,updated_date,lead_time_minutes,parent_issue_id,priority,original_estimate_minutes,time_spent_minutes,time_remaining_minutes,creator_id,creator_name,assignee_id,assignee_name,severity,component
-zentao:ZentaoTask:1:1,,,1,任务名称,任务描述<span> </span><br /><div><br 
/></div>,,TASK,devel,TODO,wait,0,,2022-09-19T01:50:37.000+00:00,,0,zentao:ZentaoStory:1:0,,0,0,0,1,devlake,5,开发乙,,
-zentao:ZentaoTask:1:2,,,2,任务名称,任务描述<span> </span><br /><div><br 
/></div>,,TASK,devel,TODO,wait,0,,2022-09-19T01:50:37.000+00:00,,0,zentao:ZentaoStory:1:0,,0,0,0,1,devlake,5,开发乙,,
-zentao:ZentaoTask:1:3,,,3,任务名称,任务描述<span> </span><br /><div><br 
/></div>,,TASK,devel,TODO,wait,0,,2022-09-19T01:50:37.000+00:00,,0,zentao:ZentaoStory:1:-1,,0,0,0,1,devlake,5,开发乙,,
+zentao:ZentaoTask:1:1,http://iwater.red:8000/api.php/v1/executions/9/tasks?limit=100&page=1,,1,任务名称,任务描述<span>
 </span><br /><div><br 
/></div>,,TASK,devel.,TODO,wait,0,,2022-09-19T01:50:37.000+00:00,,0,zentao:ZentaoStory:1:0,Middle,0,0,0,1,devlake,5,开发乙,,
+zentao:ZentaoTask:1:2,http://iwater.red:8000/api.php/v1/executions/9/tasks?limit=100&page=1,,2,任务名称,任务描述<span>
 </span><br /><div><br 
/></div>,,TASK,devel.,TODO,wait,0,,2022-09-19T01:50:37.000+00:00,,0,zentao:ZentaoStory:1:0,Middle,0,0,0,1,devlake,5,开发乙,,
+zentao:ZentaoTask:1:3,http://iwater.red:8000/api.php/v1/executions/9/tasks?limit=100&page=1,,3,任务名称,任务描述<span>
 </span><br /><div><br 
/></div>,,TASK,devel.,TODO,wait,0,,2022-09-19T01:50:37.000+00:00,,0,zentao:ZentaoStory:1:-1,Middle,0,0,0,1,devlake,5,开发乙,,
diff --git a/backend/plugins/zentao/impl/impl.go 
b/backend/plugins/zentao/impl/impl.go
index 1a725aebb..d10eb47ec 100644
--- a/backend/plugins/zentao/impl/impl.go
+++ b/backend/plugins/zentao/impl/impl.go
@@ -21,6 +21,7 @@ import (
        "fmt"
 
        "github.com/apache/incubator-devlake/core/context"
+       "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/core/runner"
@@ -57,22 +58,30 @@ func (p Zentao) SubTaskMetas() []plugin.SubTaskMeta {
        return []plugin.SubTaskMeta{
                tasks.ConvertProductMeta,
                tasks.ConvertProjectMeta,
+
                tasks.DBGetChangelogMeta,
+               tasks.ConvertChangelogMeta,
+
                tasks.CollectExecutionMeta,
                tasks.ExtractExecutionMeta,
                tasks.ConvertExecutionMeta,
+
                tasks.CollectStoryMeta,
                tasks.ExtractStoryMeta,
                tasks.ConvertStoryMeta,
+
                tasks.CollectBugMeta,
                tasks.ExtractBugMeta,
                tasks.ConvertBugMeta,
+
                tasks.CollectTaskMeta,
                tasks.ExtractTaskMeta,
                tasks.ConvertTaskMeta,
+
                tasks.CollectAccountMeta,
                tasks.ExtractAccountMeta,
                tasks.ConvertAccountMeta,
+
                tasks.CollectDepartmentMeta,
                tasks.ExtractDepartmentMeta,
                tasks.ConvertDepartmentMeta,
@@ -100,6 +109,18 @@ func (p Zentao) PrepareTaskData(taskCtx 
plugin.TaskContext, options map[string]i
                return nil, errors.Default.Wrap(err, "unable to get Zentao API 
client instance: %v")
        }
 
+       if op.ScopeConfigs == nil && op.ScopeConfigId != 0 {
+               var scopeConfig models.ZentaoScopeConfig
+               err = taskCtx.GetDal().First(&scopeConfig, dal.Where("id = ?", 
op.ScopeConfigId))
+               if err != nil && taskCtx.GetDal().IsErrorNotFound(err) {
+                       return nil, errors.BadInput.Wrap(err, "fail to get 
ScopeConfigs")
+               }
+               op.ScopeConfigs, err = tasks.MakeScopeConfigs(scopeConfig)
+               if err != nil {
+                       return nil, errors.BadInput.Wrap(err, "fail to make 
ScopeConfigs")
+               }
+       }
+
        data := &tasks.ZentaoTaskData{
                Options:   op,
                ApiClient: apiClient,
diff --git a/backend/plugins/zentao/models/archived/scope_config.go 
b/backend/plugins/zentao/models/archived/scope_config.go
new file mode 100644
index 000000000..5eb5bb0c9
--- /dev/null
+++ b/backend/plugins/zentao/models/archived/scope_config.go
@@ -0,0 +1,38 @@
+/*
+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 archived
+
+import (
+       "encoding/json"
+
+       
"github.com/apache/incubator-devlake/core/models/migrationscripts/archived"
+)
+
+type ZentaoScopeConfig struct {
+       archived.ScopeConfig `mapstructure:",squash" json:",inline" 
gorm:"embedded"`
+       ConnectionId         uint64          `mapstructure:"connectionId" 
json:"connectionId"`
+       Name                 string          
`gorm:"type:varchar(255);index:idx_name_tapd,unique" validate:"required" 
mapstructure:"name" json:"name"`
+       TypeMappings         json.RawMessage 
`mapstructure:"typeMappings,omitempty" json:"typeMappings"`
+       BugStatusMappings    json.RawMessage 
`mapstructure:"bugStatusMappings,omitempty" json:"bugStatusMappings"`
+       StoryStatusMappings  json.RawMessage 
`mapstructure:"storyStatusMappings,omitempty" json:"storyStatusMappings"`
+       TaskStatusMappings   json.RawMessage 
`mapstructure:"taskStatusMappings,omitempty" json:"taskStatusMappings"`
+}
+
+func (t ZentaoScopeConfig) TableName() string {
+       return "_tool_zentao_scope_configs"
+}
diff --git a/backend/plugins/zentao/models/bug.go 
b/backend/plugins/zentao/models/bug.go
index 664910121..1a16bfa96 100644
--- a/backend/plugins/zentao/models/bug.go
+++ b/backend/plugins/zentao/models/bug.go
@@ -160,6 +160,9 @@ type ZentaoBug struct {
        Needconfirm    bool                `json:"needconfirm"`
        StatusName     string              `json:"statusName"`
        ProductStatus  string              `json:"productStatus"`
+       Url            string              `json:"url"`
+       StdStatus      string              `json:"stdStatus" 
gorm:"type:varchar(20)"`
+       StdType        string              `json:"stdType" 
gorm:"type:varchar(20)"`
 }
 
 func (ZentaoBug) TableName() string {
diff --git 
a/backend/plugins/zentao/models/migrationscripts/20230519_add_init_changelog_tables.go
 
b/backend/plugins/zentao/models/migrationscripts/20230519_add_init_changelog_tables.go
index ec8c676c6..dcdd79628 100644
--- 
a/backend/plugins/zentao/models/migrationscripts/20230519_add_init_changelog_tables.go
+++ 
b/backend/plugins/zentao/models/migrationscripts/20230519_add_init_changelog_tables.go
@@ -56,7 +56,7 @@ func (*addInitChangelogTables) Up(basicRes context.BasicRes) 
errors.Error {
 }
 
 func (*addInitChangelogTables) Version() uint64 {
-       return 20230525000001
+       return 20230530000002
 }
 
 func (*addInitChangelogTables) Name() string {
diff --git 
a/backend/plugins/zentao/models/migrationscripts/20230601_add_scope_config.go 
b/backend/plugins/zentao/models/migrationscripts/20230601_add_scope_config.go
new file mode 100644
index 000000000..3ed1bd37c
--- /dev/null
+++ 
b/backend/plugins/zentao/models/migrationscripts/20230601_add_scope_config.go
@@ -0,0 +1,76 @@
+/*
+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 migrationscripts
+
+import (
+       "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/errors"
+       "github.com/apache/incubator-devlake/helpers/migrationhelper"
+       "github.com/apache/incubator-devlake/plugins/zentao/models/archived"
+)
+
+type addScopeConfigTables struct{}
+
+type bug20230601 struct {
+       Url       string `json:"url"`
+       StdStatus string `json:"stdStatus" gorm:"type:varchar(20)"`
+       StdType   string `json:"stdType" gorm:"type:varchar(20)"`
+}
+
+func (bug20230601) TableName() string {
+       return "_tool_zentao_bugs"
+}
+
+type story20230601 struct {
+       Url       string `json:"url"`
+       StdStatus string `json:"stdStatus" gorm:"type:varchar(20)"`
+       StdType   string `json:"stdType" gorm:"type:varchar(20)"`
+}
+
+func (story20230601) TableName() string {
+       return "_tool_zentao_stories"
+}
+
+type task20230601 struct {
+       Url       string `json:"url"`
+       StdStatus string `json:"stdStatus" gorm:"type:varchar(20)"`
+       StdType   string `json:"stdType" gorm:"type:varchar(20)"`
+}
+
+func (task20230601) TableName() string {
+       return "_tool_zentao_tasks"
+}
+
+func (*addScopeConfigTables) Up(basicRes context.BasicRes) errors.Error {
+
+       return migrationhelper.AutoMigrateTables(
+               basicRes,
+               &archived.ZentaoScopeConfig{},
+               &bug20230601{},
+               &story20230601{},
+               &task20230601{},
+       )
+}
+
+func (*addScopeConfigTables) Version() uint64 {
+       return 20230601000001
+}
+
+func (*addScopeConfigTables) Name() string {
+       return "zentao add scope config tables"
+}
diff --git a/backend/plugins/zentao/models/migrationscripts/register.go 
b/backend/plugins/zentao/models/migrationscripts/register.go
index e71dac9f4..df08cb6a2 100644
--- a/backend/plugins/zentao/models/migrationscripts/register.go
+++ b/backend/plugins/zentao/models/migrationscripts/register.go
@@ -27,5 +27,6 @@ func All() []plugin.MigrationScript {
                new(addInitTables),
                new(addInitChangelogTables),
                new(addIssueCommitsTables),
+               new(addScopeConfigTables),
        }
 }
diff --git a/backend/plugins/zentao/models/remote_db.go 
b/backend/plugins/zentao/models/remote_db.go
index bb5439458..a1b1f09fd 100644
--- a/backend/plugins/zentao/models/remote_db.go
+++ b/backend/plugins/zentao/models/remote_db.go
@@ -86,7 +86,7 @@ func (ah *ZentaoRemoteDbActionHistory) Convert() 
*ZentaoChangelogCom {
                },
                &ZentaoChangelogDetail{
                        Id:          int64(ah.HistoryId),
-                       ChangelogId: int64(ah.Id),
+                       ChangelogId: int64(ah.ActionId),
                        Field:       ah.Field,
                        Old:         ah.Old,
                        New:         ah.New,
diff --git a/backend/plugins/zentao/models/scope_config.go 
b/backend/plugins/zentao/models/scope_config.go
new file mode 100644
index 000000000..d5a194e35
--- /dev/null
+++ b/backend/plugins/zentao/models/scope_config.go
@@ -0,0 +1,38 @@
+/*
+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 models
+
+import (
+       "encoding/json"
+
+       "github.com/apache/incubator-devlake/core/models/common"
+)
+
+type ZentaoScopeConfig struct {
+       common.ScopeConfig  `mapstructure:",squash" json:",inline" 
gorm:"embedded"`
+       ConnectionId        uint64          `mapstructure:"connectionId" 
json:"connectionId"`
+       Name                string          
`gorm:"type:varchar(255);index:idx_name_tapd,unique" validate:"required" 
mapstructure:"name" json:"name"`
+       TypeMappings        json.RawMessage 
`mapstructure:"typeMappings,omitempty" json:"typeMappings"`
+       BugStatusMappings   json.RawMessage 
`mapstructure:"bugStatusMappings,omitempty" json:"bugStatusMappings"`
+       StoryStatusMappings json.RawMessage 
`mapstructure:"storyStatusMappings,omitempty" json:"storyStatusMappings"`
+       TaskStatusMappings  json.RawMessage 
`mapstructure:"taskStatusMappings,omitempty" json:"taskStatusMappings"`
+}
+
+func (t ZentaoScopeConfig) TableName() string {
+       return "_tool_zentao_scope_configs"
+}
diff --git a/backend/plugins/zentao/models/story.go 
b/backend/plugins/zentao/models/story.go
index 274fec596..84334f568 100644
--- a/backend/plugins/zentao/models/story.go
+++ b/backend/plugins/zentao/models/story.go
@@ -137,6 +137,9 @@ type ZentaoStory struct {
        Deleted          bool                `json:"deleted"`
        PriOrder         string              `json:"priOrder"`
        PlanTitle        string              `json:"planTitle"`
+       Url              string              `json:"url"`
+       StdStatus        string              `json:"stdStatus" 
gorm:"type:varchar(20)"`
+       StdType          string              `json:"stdType" 
gorm:"type:varchar(20)"`
 }
 
 func (ZentaoStory) TableName() string {
diff --git a/backend/plugins/zentao/models/task.go 
b/backend/plugins/zentao/models/task.go
index 43a20be93..18d3fc2f5 100644
--- a/backend/plugins/zentao/models/task.go
+++ b/backend/plugins/zentao/models/task.go
@@ -158,6 +158,9 @@ type ZentaoTask struct {
        PriOrder           string              `json:"priOrder"`
        NeedConfirm        bool                `json:"needConfirm"`
        Progress           float64             `json:"progress"`
+       Url                string              `json:"url"`
+       StdStatus          string              `json:"stdStatus" 
gorm:"type:varchar(20)"`
+       StdType            string              `json:"stdType" 
gorm:"type:varchar(20)"`
 }
 
 func (ZentaoTask) TableName() string {
diff --git a/backend/plugins/zentao/tasks/bug_convertor.go 
b/backend/plugins/zentao/tasks/bug_convertor.go
index 3ae03828b..51fafc467 100644
--- a/backend/plugins/zentao/tasks/bug_convertor.go
+++ b/backend/plugins/zentao/tasks/bug_convertor.go
@@ -74,28 +74,26 @@ func ConvertBug(taskCtx plugin.SubTaskContext) errors.Error 
{
                                DomainEntity: domainlayer.DomainEntity{
                                        Id: 
bugIdGen.Generate(toolEntity.ConnectionId, toolEntity.ID),
                                },
-                               IssueKey:       
strconv.FormatInt(toolEntity.ID, 10),
-                               Title:          toolEntity.Title,
-                               Type:           ticket.BUG,
-                               OriginalType:   toolEntity.Type,
-                               OriginalStatus: toolEntity.Status,
-                               ResolutionDate: 
toolEntity.ClosedDate.ToNullableTime(),
-                               CreatedDate:    
toolEntity.OpenedDate.ToNullableTime(),
-                               UpdatedDate:    
toolEntity.LastEditedDate.ToNullableTime(),
-                               ParentIssueId:  
storyIdGen.Generate(data.Options.ConnectionId, toolEntity.Story),
-                               Priority:       string(rune(toolEntity.Pri)),
-                               CreatorId:      
strconv.FormatInt(toolEntity.OpenedById, 10),
-                               CreatorName:    toolEntity.OpenedByName,
-                               AssigneeId:     
strconv.FormatInt(toolEntity.AssignedToId, 10),
-                               AssigneeName:   toolEntity.AssignedToName,
-                               Severity:       
string(rune(toolEntity.Severity)),
-                       }
-                       switch toolEntity.Status {
-                       case "resolved":
-                               domainEntity.Status = ticket.DONE
-                       default:
-                               domainEntity.Status = ticket.IN_PROGRESS
+                               IssueKey:        
strconv.FormatInt(toolEntity.ID, 10),
+                               Title:           toolEntity.Title,
+                               Type:            ticket.BUG,
+                               OriginalType:    toolEntity.Type,
+                               OriginalStatus:  toolEntity.Status,
+                               ResolutionDate:  
toolEntity.ClosedDate.ToNullableTime(),
+                               CreatedDate:     
toolEntity.OpenedDate.ToNullableTime(),
+                               UpdatedDate:     
toolEntity.LastEditedDate.ToNullableTime(),
+                               ParentIssueId:   
storyIdGen.Generate(data.Options.ConnectionId, toolEntity.Story),
+                               Priority:        getPriority(toolEntity.Pri),
+                               CreatorId:       
strconv.FormatInt(toolEntity.OpenedById, 10),
+                               CreatorName:     toolEntity.OpenedByName,
+                               AssigneeId:      
strconv.FormatInt(toolEntity.AssignedToId, 10),
+                               AssigneeName:    toolEntity.AssignedToName,
+                               Severity:        
string(rune(toolEntity.Severity)),
+                               Url:             toolEntity.Url,
+                               OriginalProject: getOriginalProject(data),
+                               Status:          toolEntity.StdStatus,
                        }
+
                        if toolEntity.ClosedDate != nil {
                                domainEntity.LeadTimeMinutes = 
int64(toolEntity.ClosedDate.ToNullableTime().Sub(toolEntity.OpenedDate.ToTime()).Minutes())
                        }
diff --git a/backend/plugins/zentao/tasks/bug_extractor.go 
b/backend/plugins/zentao/tasks/bug_extractor.go
index eff39b734..3f3396d6e 100644
--- a/backend/plugins/zentao/tasks/bug_extractor.go
+++ b/backend/plugins/zentao/tasks/bug_extractor.go
@@ -21,6 +21,7 @@ import (
        "encoding/json"
 
        "github.com/apache/incubator-devlake/core/errors"
+       "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/zentao/models"
@@ -44,6 +45,9 @@ func ExtractBug(taskCtx plugin.SubTaskContext) errors.Error {
                return nil
        }
 
+       statusMappings := getBugStatusMapping(data)
+       stdTypeMappings := getStdTypeMappings(data)
+
        extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
                RawDataSubTaskArgs: api.RawDataSubTaskArgs{
                        Ctx: taskCtx,
@@ -129,7 +133,23 @@ func ExtractBug(taskCtx plugin.SubTaskContext) 
errors.Error {
                                Needconfirm:    res.Needconfirm,
                                StatusName:     res.StatusName,
                                ProductStatus:  res.ProductStatus,
+                               Url:            row.Url,
+                       }
+
+                       bug.StdType = stdTypeMappings[bug.Type]
+                       if bug.StdType == "" {
+                               bug.StdType = ticket.BUG
                        }
+
+                       if len(statusMappings) != 0 {
+                               bug.StdStatus = statusMappings[bug.Status]
+                       } else {
+                               bug.StdStatus = 
ticket.GetStatus(&ticket.StatusRule{
+                                       Done:    []string{"resolved"},
+                                       Default: ticket.IN_PROGRESS,
+                               }, bug.Status)
+                       }
+
                        results := make([]interface{}, 0)
                        results = append(results, bug)
                        return results, nil
diff --git a/backend/plugins/zentao/tasks/changelog_convertor.go 
b/backend/plugins/zentao/tasks/changelog_convertor.go
new file mode 100644
index 000000000..d67eaee0b
--- /dev/null
+++ b/backend/plugins/zentao/tasks/changelog_convertor.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 tasks
+
+import (
+       "fmt"
+       "reflect"
+
+       "github.com/apache/incubator-devlake/core/dal"
+       "github.com/apache/incubator-devlake/core/errors"
+       "github.com/apache/incubator-devlake/core/models/common"
+       "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/helpers/pluginhelper/api"
+       "github.com/apache/incubator-devlake/plugins/zentao/models"
+)
+
+var _ plugin.SubTaskEntryPoint = ConvertChangelog
+
+var ConvertChangelogMeta = plugin.SubTaskMeta{
+       Name:             "ConvertChangelog",
+       EntryPoint:       ConvertChangelog,
+       EnabledByDefault: true,
+       Description:      "convert Zentao changelog",
+       DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+type ZentaoChangelogSelect struct {
+       common.NoPKModel `json:"-"`
+
+       models.ZentaoChangelog
+       ChangelogId int64  `json:"changelogId" mapstructure:"changelogId" 
gorm:"primaryKey;type:BIGINT  NOT NULL"`
+       Field       string `json:"field" mapstructure:"field"`
+       Old         string `json:"old" mapstructure:"old"`
+       New         string `json:"new" mapstructure:"new"`
+       Diff        string `json:"diff" mapstructure:"diff"`
+
+       Account  string `json:"account" gorm:"type:varchar(100);index"`
+       Avatar   string `json:"avatar" gorm:"type:varchar(255)"`
+       Realname string `json:"realname" gorm:"type:varchar(100);index"`
+       Role     string `json:"role" gorm:"type:varchar(100);index"`
+       Dept     int64  `json:"dept" gorm:"type:BIGINT  NOT NULL;index"`
+
+       CID  int64 `json:"cid" mapstructure:"cid" gorm:"column:cid"`
+       CDID int64 `json:"cdid" mapstructure:"cdid" gorm:"column:cdid"`
+       AID  int64 `json:"aid" mapstructure:"aid" gorm:"column:aid"`
+}
+
+func ConvertChangelog(taskCtx plugin.SubTaskContext) errors.Error {
+       data := taskCtx.GetData().(*ZentaoTaskData)
+       db := taskCtx.GetDal()
+       changelogIdGen := 
didgen.NewDomainIdGenerator(&models.ZentaoChangelogDetail{})
+       cn := models.ZentaoChangelog{}.TableName()
+       cdn := models.ZentaoChangelogDetail{}.TableName()
+       an := models.ZentaoAccount{}.TableName()
+       cursor, err := db.Cursor(
+               dal.Select(fmt.Sprintf("*,%s.id cid,%s.id cdid,%s.id aid ", cn, 
cdn, an)),
+               dal.From(&models.ZentaoChangelog{}),
+               dal.Join(fmt.Sprintf("LEFT JOIN %s on %s.changelog_id = %s.id", 
cdn, cdn, cn)),
+               dal.Join(fmt.Sprintf("LEFT JOIN %s on %s.realname = %s.actor", 
an, an, cn)),
+               dal.Where(fmt.Sprintf(`%s.product = ? and %s.project = ? and 
%s.connection_id = ?`,
+                       cn, cn, cn),
+                       data.Options.ProductId,
+                       data.Options.ProjectId,
+                       data.Options.ConnectionId),
+       )
+       if err != nil {
+               return err
+       }
+       defer cursor.Close()
+
+       convertor, err := api.NewDataConverter(api.DataConverterArgs{
+               InputRowType: reflect.TypeOf(ZentaoChangelogSelect{}),
+               Input:        cursor,
+               RawDataSubTaskArgs: api.RawDataSubTaskArgs{
+                       Ctx: taskCtx,
+                       Params: ZentaoApiParams{
+                               ConnectionId: data.Options.ConnectionId,
+                               ProductId:    data.Options.ProductId,
+                               ProjectId:    data.Options.ProjectId,
+                       },
+                       Table: RAW_ACCOUNT_TABLE,
+               },
+               Convert: func(inputRow interface{}) ([]interface{}, 
errors.Error) {
+                       cl := inputRow.(*ZentaoChangelogSelect)
+
+                       domainCl := &ticket.IssueChangelogs{
+                               DomainEntity: domainlayer.DomainEntity{
+                                       Id: 
changelogIdGen.Generate(data.Options.ConnectionId, cl.CID, cl.CDID),
+                               },
+                               IssueId:           fmt.Sprintf("%d", 
cl.ObjectId),
+                               AuthorId:          fmt.Sprintf("%d", cl.AID),
+                               AuthorName:        cl.Actor,
+                               FieldId:           cl.Field,
+                               FieldName:         cl.Field,
+                               OriginalFromValue: cl.Old,
+                               OriginalToValue:   cl.New,
+                               FromValue:         cl.Old,
+                               ToValue:           cl.New,
+                               CreatedDate:       cl.Date,
+                       }
+
+                       return []interface{}{
+                               domainCl,
+                       }, nil
+               },
+       })
+
+       if err != nil {
+               return err
+       }
+
+       return convertor.Execute()
+}
diff --git a/backend/plugins/zentao/tasks/execution_convertor.go 
b/backend/plugins/zentao/tasks/execution_convertor.go
index 4c9866189..735f86e4e 100644
--- a/backend/plugins/zentao/tasks/execution_convertor.go
+++ b/backend/plugins/zentao/tasks/execution_convertor.go
@@ -18,6 +18,8 @@ limitations under the License.
 package tasks
 
 import (
+       "reflect"
+
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/models/domainlayer"
@@ -26,7 +28,6 @@ import (
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/zentao/models"
-       "reflect"
 )
 
 var _ plugin.SubTaskEntryPoint = ConvertExecutions
@@ -89,8 +90,8 @@ func ConvertExecutions(taskCtx plugin.SubTaskContext) 
errors.Error {
                                Status:          domainStatus,
                                StartedDate:     
toolExecution.RealBegan.ToNullableTime(),
                                EndedDate:       
toolExecution.RealEnd.ToNullableTime(),
-                               CompletedDate:   
toolExecution.ClosedDate.ToNullableTime(),
-                               OriginalBoardID: 
projectIdGen.Generate(toolExecution.ConnectionId, toolExecution.Id),
+                               CompletedDate:   
toolExecution.PlanEnd.ToNullableTime(),
+                               OriginalBoardID: 
projectIdGen.Generate(toolExecution.ConnectionId, data.Options.ProjectId),
                        }
                        boardSprint := &ticket.BoardSprint{
                                BoardId:  
projectIdGen.Generate(toolExecution.ConnectionId, toolExecution.Id),
diff --git a/backend/plugins/zentao/tasks/product_convertor.go 
b/backend/plugins/zentao/tasks/product_convertor.go
index 3ddfe4bd7..fc3a645eb 100644
--- a/backend/plugins/zentao/tasks/product_convertor.go
+++ b/backend/plugins/zentao/tasks/product_convertor.go
@@ -18,6 +18,7 @@ limitations under the License.
 package tasks
 
 import (
+       "fmt"
        "reflect"
 
        "github.com/apache/incubator-devlake/core/dal"
@@ -69,6 +70,8 @@ func ConvertProducts(taskCtx plugin.SubTaskContext) 
errors.Error {
                Convert: func(inputRow interface{}) ([]interface{}, 
errors.Error) {
                        toolProduct := inputRow.(*models.ZentaoProduct)
 
+                       data.ProductName = toolProduct.Name
+
                        domainBoard := &ticket.Board{
                                DomainEntity: domainlayer.DomainEntity{
                                        Id: 
boardIdGen.Generate(toolProduct.ConnectionId, toolProduct.Id),
@@ -77,6 +80,7 @@ func ConvertProducts(taskCtx plugin.SubTaskContext) 
errors.Error {
                                Description: toolProduct.Description,
                                CreatedDate: 
toolProduct.CreatedDate.ToNullableTime(),
                                Type:        toolProduct.Type + "/" + 
toolProduct.ProductType,
+                               Url:         
fmt.Sprintf("/product-index-%d.html", data.Options.ProductId),
                        }
                        results := make([]interface{}, 0)
                        results = append(results, domainBoard)
diff --git a/backend/plugins/zentao/tasks/project_convertor.go 
b/backend/plugins/zentao/tasks/project_convertor.go
index 8bb555372..81f2fddfe 100644
--- a/backend/plugins/zentao/tasks/project_convertor.go
+++ b/backend/plugins/zentao/tasks/project_convertor.go
@@ -18,6 +18,7 @@ limitations under the License.
 package tasks
 
 import (
+       "fmt"
        "reflect"
 
        "github.com/apache/incubator-devlake/core/dal"
@@ -69,6 +70,8 @@ func ConvertProjects(taskCtx plugin.SubTaskContext) 
errors.Error {
                Convert: func(inputRow interface{}) ([]interface{}, 
errors.Error) {
                        toolProject := inputRow.(*models.ZentaoProject)
 
+                       data.ProjectName = toolProject.Name
+
                        domainBoard := &ticket.Board{
                                DomainEntity: domainlayer.DomainEntity{
                                        Id: 
boardIdGen.Generate(toolProject.ConnectionId, toolProject.Id),
@@ -77,6 +80,7 @@ func ConvertProjects(taskCtx plugin.SubTaskContext) 
errors.Error {
                                Description: toolProject.Description,
                                CreatedDate: 
toolProject.OpenedDate.ToNullableTime(),
                                Type:        toolProject.Type + "/" + 
toolProject.ProjectType,
+                               Url:         
fmt.Sprintf("/project-index-%d.html", data.Options.ProjectId),
                        }
                        results := make([]interface{}, 0)
                        results = append(results, domainBoard)
diff --git a/backend/plugins/zentao/tasks/shared.go 
b/backend/plugins/zentao/tasks/shared.go
index 3e71d34b5..d9fcc69c4 100644
--- a/backend/plugins/zentao/tasks/shared.go
+++ b/backend/plugins/zentao/tasks/shared.go
@@ -18,10 +18,12 @@ limitations under the License.
 package tasks
 
 import (
+       "net/http"
+       "strings"
+
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/zentao/models"
-       "net/http"
 )
 
 func GetTotalPagesFromResponse(res *http.Response, args *api.ApiCollectorArgs) 
(int, errors.Error) {
@@ -50,3 +52,93 @@ func getAccountName(account *models.ZentaoAccount) string {
        }
        return ""
 }
+
+// get the Priority string for zentao
+func getPriority(pri int) string {
+       switch pri {
+       case 2:
+               return "High"
+       case 3:
+               return "Middle"
+       case 4:
+               return "Low"
+       default:
+               if pri <= 1 {
+                       return "VeryHigh"
+               }
+               if pri >= 5 {
+                       return "VeryLow"
+               }
+       }
+       return "Error"
+}
+
+func getOriginalProject(data *ZentaoTaskData) string {
+       if data.Options.ProjectId != 0 {
+               return data.ProjectName
+       }
+       if data.Options.ProductId != 0 {
+               return data.ProductName
+       }
+       return ""
+}
+
+// getBugStatusMapping creates a map of original status values to bug issue 
standard status values
+// based on the provided ZentaoTaskData. It returns the created map.
+func getBugStatusMapping(data *ZentaoTaskData) map[string]string {
+       stdStatusMappings := make(map[string]string)
+       if data.Options.ScopeConfigs == nil {
+               return stdStatusMappings
+       }
+       mapping := data.Options.ScopeConfigs.BugStatusMappings
+       // Map original status values to standard status values
+       for userStatus, stdStatus := range mapping {
+               stdStatusMappings[userStatus] = strings.ToUpper(stdStatus)
+       }
+       return stdStatusMappings
+}
+
+// getStoryStatusMapping creates a map of original status values to story 
issue standard status values
+// based on the provided ZentaoTaskData. It returns the created map.
+func getStoryStatusMapping(data *ZentaoTaskData) map[string]string {
+       stdStatusMappings := make(map[string]string)
+       if data.Options.ScopeConfigs == nil {
+               return stdStatusMappings
+       }
+       mapping := data.Options.ScopeConfigs.StoryStatusMappings
+       // Map original status values to standard status values
+       for userStatus, stdStatus := range mapping {
+               stdStatusMappings[userStatus] = strings.ToUpper(stdStatus)
+       }
+       return stdStatusMappings
+}
+
+// getTaskStatusMapping creates a map of original status values to task issue 
standard status values
+// based on the provided ZentaoTaskData. It returns the created map.
+func getTaskStatusMapping(data *ZentaoTaskData) map[string]string {
+       stdStatusMappings := make(map[string]string)
+       if data.Options.ScopeConfigs == nil {
+               return stdStatusMappings
+       }
+       mapping := data.Options.ScopeConfigs.TaskStatusMappings
+       // Map original status values to standard status values
+       for userStatus, stdStatus := range mapping {
+               stdStatusMappings[userStatus] = strings.ToUpper(stdStatus)
+       }
+       return stdStatusMappings
+}
+
+// getStdTypeMappings creates a map of user type to standard type based on the 
provided ZentaoTaskData.
+// It returns the created map.
+func getStdTypeMappings(data *ZentaoTaskData) map[string]string {
+       stdTypeMappings := make(map[string]string)
+       if data.Options.ScopeConfigs == nil {
+               return stdTypeMappings
+       }
+       mapping := data.Options.ScopeConfigs.TypeMappings
+       // Map user types to standard types
+       for userType, stdType := range mapping {
+               stdTypeMappings[userType] = strings.ToUpper(stdType)
+       }
+       return stdTypeMappings
+}
diff --git a/backend/plugins/zentao/tasks/story_convertor.go 
b/backend/plugins/zentao/tasks/story_convertor.go
index bfa9d3af5..cf1097e1b 100644
--- a/backend/plugins/zentao/tasks/story_convertor.go
+++ b/backend/plugins/zentao/tasks/story_convertor.go
@@ -18,6 +18,9 @@ limitations under the License.
 package tasks
 
 import (
+       "reflect"
+       "strconv"
+
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/models/domainlayer"
@@ -26,8 +29,6 @@ import (
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/zentao/models"
-       "reflect"
-       "strconv"
 )
 
 var _ plugin.SubTaskEntryPoint = ConvertStory
@@ -73,28 +74,27 @@ func ConvertStory(taskCtx plugin.SubTaskContext) 
errors.Error {
                                DomainEntity: domainlayer.DomainEntity{
                                        Id: 
storyIdGen.Generate(toolEntity.ConnectionId, toolEntity.ID),
                                },
-                               IssueKey:       
strconv.FormatInt(toolEntity.ID, 10),
-                               Title:          toolEntity.Title,
-                               Type:           ticket.REQUIREMENT,
-                               OriginalType:   toolEntity.Type,
-                               OriginalStatus: toolEntity.Stage,
-                               ResolutionDate: 
toolEntity.ClosedDate.ToNullableTime(),
-                               CreatedDate:    
toolEntity.OpenedDate.ToNullableTime(),
-                               UpdatedDate:    
toolEntity.LastEditedDate.ToNullableTime(),
-                               ParentIssueId:  
storyIdGen.Generate(data.Options.ConnectionId, toolEntity.Parent),
-                               Priority:       string(rune(toolEntity.Pri)),
-                               CreatorId:      
strconv.FormatInt(toolEntity.OpenedById, 10),
-                               CreatorName:    toolEntity.OpenedByName,
-                               AssigneeId:     
strconv.FormatInt(toolEntity.AssignedToId, 10),
-                               AssigneeName:   toolEntity.AssignedToName,
+                               IssueKey:        
strconv.FormatInt(toolEntity.ID, 10),
+                               Title:           toolEntity.Title,
+                               Type:            ticket.REQUIREMENT,
+                               OriginalType:    toolEntity.Type + "." + 
toolEntity.Category,
+                               OriginalStatus:  toolEntity.Status + "-" + 
toolEntity.Stage,
+                               ResolutionDate:  
toolEntity.ClosedDate.ToNullableTime(),
+                               CreatedDate:     
toolEntity.OpenedDate.ToNullableTime(),
+                               UpdatedDate:     
toolEntity.LastEditedDate.ToNullableTime(),
+                               ParentIssueId:   
storyIdGen.Generate(data.Options.ConnectionId, toolEntity.Parent),
+                               Priority:        getPriority(toolEntity.Pri),
+                               CreatorId:       
strconv.FormatInt(toolEntity.OpenedById, 10),
+                               CreatorName:     toolEntity.OpenedByName,
+                               AssigneeId:      
strconv.FormatInt(toolEntity.AssignedToId, 10),
+                               AssigneeName:    toolEntity.AssignedToName,
+                               Url:             toolEntity.Url,
+                               OriginalProject: getOriginalProject(data),
+                               Status:          toolEntity.StdStatus,
                        }
-                       switch toolEntity.Stage {
-                       case "closed":
-                               domainEntity.Status = ticket.DONE
-                       case "wait":
-                               domainEntity.Status = ticket.TODO
-                       default:
-                               domainEntity.Status = ticket.IN_PROGRESS
+
+                       if domainEntity.OriginalStatus == "closed-closed" {
+                               domainEntity.OriginalStatus = "closed"
                        }
                        var results []interface{}
                        if domainEntity.AssigneeId != "" {
@@ -105,6 +105,7 @@ func ConvertStory(taskCtx plugin.SubTaskContext) 
errors.Error {
                                }
                                results = append(results, issueAssignee)
                        }
+
                        if toolEntity.ClosedDate != nil {
                                domainEntity.LeadTimeMinutes = 
int64(toolEntity.ClosedDate.ToNullableTime().Sub(toolEntity.OpenedDate.ToTime()).Minutes())
                        }
diff --git a/backend/plugins/zentao/tasks/story_extractor.go 
b/backend/plugins/zentao/tasks/story_extractor.go
index e4e633fb5..46674fdd0 100644
--- a/backend/plugins/zentao/tasks/story_extractor.go
+++ b/backend/plugins/zentao/tasks/story_extractor.go
@@ -21,6 +21,7 @@ import (
        "encoding/json"
 
        "github.com/apache/incubator-devlake/core/errors"
+       "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/zentao/models"
@@ -44,6 +45,9 @@ func ExtractStory(taskCtx plugin.SubTaskContext) errors.Error 
{
                return nil
        }
 
+       statusMappings := getStoryStatusMapping(data)
+       stdTypeMappings := getStdTypeMappings(data)
+
        extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
                RawDataSubTaskArgs: api.RawDataSubTaskArgs{
                        Ctx: taskCtx,
@@ -116,7 +120,24 @@ func ExtractStory(taskCtx plugin.SubTaskContext) 
errors.Error {
                                Deleted:          res.Deleted,
                                PriOrder:         res.PriOrder,
                                PlanTitle:        res.PlanTitle,
+                               Url:              row.Url,
+                       }
+
+                       story.StdType = stdTypeMappings[story.Type]
+                       if story.StdType == "" {
+                               story.StdType = ticket.REQUIREMENT
                        }
+
+                       if len(statusMappings) != 0 {
+                               story.StdStatus = statusMappings[story.Stage]
+                       } else {
+                               story.StdStatus = 
ticket.GetStatus(&ticket.StatusRule{
+                                       Done:    []string{"closed"},
+                                       Todo:    []string{"wait"},
+                                       Default: ticket.IN_PROGRESS,
+                               }, story.Stage)
+                       }
+
                        results := make([]interface{}, 0)
                        results = append(results, story)
                        return results, nil
diff --git a/backend/plugins/zentao/tasks/task_convertor.go 
b/backend/plugins/zentao/tasks/task_convertor.go
index 22f44a9f4..75273c951 100644
--- a/backend/plugins/zentao/tasks/task_convertor.go
+++ b/backend/plugins/zentao/tasks/task_convertor.go
@@ -18,6 +18,9 @@ limitations under the License.
 package tasks
 
 import (
+       "reflect"
+       "strconv"
+
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/models/domainlayer"
@@ -26,8 +29,6 @@ import (
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/zentao/models"
-       "reflect"
-       "strconv"
 )
 
 var _ plugin.SubTaskEntryPoint = ConvertTask
@@ -73,30 +74,26 @@ func ConvertTask(taskCtx plugin.SubTaskContext) 
errors.Error {
                                DomainEntity: domainlayer.DomainEntity{
                                        Id: 
taskIdGen.Generate(toolEntity.ConnectionId, toolEntity.ID),
                                },
-                               IssueKey:       
strconv.FormatInt(toolEntity.ID, 10),
-                               Title:          toolEntity.Name,
-                               Description:    toolEntity.Description,
-                               Type:           ticket.TASK,
-                               OriginalType:   toolEntity.Type,
-                               OriginalStatus: toolEntity.Status,
-                               ResolutionDate: 
toolEntity.ClosedDate.ToNullableTime(),
-                               CreatedDate:    
toolEntity.OpenedDate.ToNullableTime(),
-                               UpdatedDate:    
toolEntity.LastEditedDate.ToNullableTime(),
-                               ParentIssueId:  
storyIdGen.Generate(data.Options.ConnectionId, toolEntity.Parent),
-                               Priority:       string(rune(toolEntity.Pri)),
-                               CreatorId:      
strconv.FormatInt(toolEntity.OpenedById, 10),
-                               CreatorName:    toolEntity.OpenedByName,
-                               AssigneeId:     
strconv.FormatInt(toolEntity.AssignedToId, 10),
-                               AssigneeName:   toolEntity.AssignedToName,
-                       }
-                       switch toolEntity.Status {
-                       case "done", "closed", "cancel":
-                               domainEntity.Status = ticket.DONE
-                       case "wait":
-                               domainEntity.Status = ticket.TODO
-                       default:
-                               domainEntity.Status = ticket.IN_PROGRESS
+                               IssueKey:        
strconv.FormatInt(toolEntity.ID, 10),
+                               Title:           toolEntity.Name,
+                               Description:     toolEntity.Description,
+                               Type:            ticket.TASK,
+                               OriginalType:    toolEntity.Type + "." + 
toolEntity.Mode,
+                               OriginalStatus:  toolEntity.Status,
+                               ResolutionDate:  
toolEntity.ClosedDate.ToNullableTime(),
+                               CreatedDate:     
toolEntity.OpenedDate.ToNullableTime(),
+                               UpdatedDate:     
toolEntity.LastEditedDate.ToNullableTime(),
+                               ParentIssueId:   
storyIdGen.Generate(data.Options.ConnectionId, toolEntity.Parent),
+                               Priority:        getPriority(toolEntity.Pri),
+                               CreatorId:       
strconv.FormatInt(toolEntity.OpenedById, 10),
+                               CreatorName:     toolEntity.OpenedByName,
+                               AssigneeId:      
strconv.FormatInt(toolEntity.AssignedToId, 10),
+                               AssigneeName:    toolEntity.AssignedToName,
+                               Url:             toolEntity.Url,
+                               OriginalProject: getOriginalProject(data),
+                               Status:          toolEntity.StdStatus,
                        }
+
                        if toolEntity.ClosedDate != nil {
                                domainEntity.LeadTimeMinutes = 
int64(toolEntity.ClosedDate.ToNullableTime().Sub(toolEntity.OpenedDate.ToTime()).Minutes())
                        }
diff --git a/backend/plugins/zentao/tasks/task_data.go 
b/backend/plugins/zentao/tasks/task_data.go
index 9632e94f6..d406f4868 100644
--- a/backend/plugins/zentao/tasks/task_data.go
+++ b/backend/plugins/zentao/tasks/task_data.go
@@ -18,12 +18,14 @@ limitations under the License.
 package tasks
 
 import (
+       "encoding/json"
        "fmt"
        "time"
 
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
        helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+       "github.com/apache/incubator-devlake/plugins/zentao/models"
        "github.com/mitchellh/mapstructure"
 )
 
@@ -41,17 +43,69 @@ type ZentaoOptions struct {
        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"`
+       TimeAfter     string              `json:"timeAfter" 
mapstructure:"timeAfter,omitempty"`
+       ScopeConfigId uint64              `json:"scopeConfigId" 
mapstructure:"scopeConfigId,omitempty"`
+       ScopeConfigs  *ZentaoScopeConfigs `json:"scopeConfigs" 
mapstructure:"scopeConfigs,omitempty"`
+}
+
+type TypeMappings map[string]string
+
+type StatusMappings map[string]string
 
+type ZentaoScopeConfigs struct {
+       TypeMappings        TypeMappings   `json:"typeMappings"`
+       BugStatusMappings   StatusMappings `json:"bugStatusMappings"`
+       StoryStatusMappings StatusMappings `json:"storyStatusMappings"`
+       TaskStatusMappings  StatusMappings `json:"taskStatusMappings"`
+}
+
+func MakeScopeConfigs(rule models.ZentaoScopeConfig) (*ZentaoScopeConfigs, 
errors.Error) {
+       var bugStatusMapping StatusMappings
+       var storyStatusMapping StatusMappings
+       var taskStatusMapping StatusMappings
+       var typeMapping TypeMappings
+       var err error
+       if len(rule.TypeMappings) > 0 {
+               err = json.Unmarshal(rule.TypeMappings, &typeMapping)
+               if err != nil {
+                       return nil, errors.Default.Wrap(err, "unable to 
unmarshal the typeMapping")
+               }
+       }
+       if len(rule.BugStatusMappings) > 0 {
+               err = json.Unmarshal(rule.BugStatusMappings, &bugStatusMapping)
+               if err != nil {
+                       return nil, errors.Default.Wrap(err, "unable to 
unmarshal the statusMapping")
+               }
+       }
+       if len(rule.StoryStatusMappings) > 0 {
+               err = json.Unmarshal(rule.StoryStatusMappings, 
&storyStatusMapping)
+               if err != nil {
+                       return nil, errors.Default.Wrap(err, "unable to 
unmarshal the statusMapping")
+               }
+       }
+       if len(rule.TaskStatusMappings) > 0 {
+               err = json.Unmarshal(rule.TaskStatusMappings, 
&taskStatusMapping)
+               if err != nil {
+                       return nil, errors.Default.Wrap(err, "unable to 
unmarshal the statusMapping")
+               }
+       }
+       result := &ZentaoScopeConfigs{
+               TypeMappings:        typeMapping,
+               BugStatusMappings:   bugStatusMapping,
+               StoryStatusMappings: storyStatusMapping,
+               TaskStatusMappings:  taskStatusMapping,
+       }
+       return result, nil
 }
 
 type ZentaoTaskData struct {
-       Options   *ZentaoOptions
-       RemoteDb  dal.Dal
-       TimeAfter *time.Time
-       ApiClient *helper.ApiAsyncClient
+       Options  *ZentaoOptions
+       RemoteDb dal.Dal
+
+       TimeAfter   *time.Time
+       ProjectName string
+       ProductName string
+       ApiClient   *helper.ApiAsyncClient
 }
 
 func DecodeAndValidateTaskOptions(options map[string]interface{}) 
(*ZentaoOptions, error) {
diff --git a/backend/plugins/zentao/tasks/task_extractor.go 
b/backend/plugins/zentao/tasks/task_extractor.go
index 7d6efc18a..c1935f7e9 100644
--- a/backend/plugins/zentao/tasks/task_extractor.go
+++ b/backend/plugins/zentao/tasks/task_extractor.go
@@ -21,6 +21,7 @@ import (
        "encoding/json"
 
        "github.com/apache/incubator-devlake/core/errors"
+       "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/zentao/models"
@@ -44,6 +45,9 @@ func ExtractTask(taskCtx plugin.SubTaskContext) errors.Error {
                return nil
        }
 
+       statusMappings := getTaskStatusMapping(data)
+       stdTypeMappings := getStdTypeMappings(data)
+
        extractor, err := api.NewApiExtractor(api.ApiExtractorArgs{
                RawDataSubTaskArgs: api.RawDataSubTaskArgs{
                        Ctx: taskCtx,
@@ -128,7 +132,24 @@ func ExtractTask(taskCtx plugin.SubTaskContext) 
errors.Error {
                                NeedConfirm:        res.NeedConfirm,
                                //ProductType:        res.ProductType,
                                Progress: res.Progress,
+                               Url:      row.Url,
+                       }
+
+                       task.StdType = stdTypeMappings[task.Type]
+                       if task.StdType == "" {
+                               task.StdType = ticket.TASK
                        }
+
+                       if len(statusMappings) != 0 {
+                               task.StdStatus = statusMappings[task.Status]
+                       } else {
+                               task.StdStatus = 
ticket.GetStatus(&ticket.StatusRule{
+                                       Done:    []string{"done", "closed", 
"cancel"},
+                                       Todo:    []string{"wait"},
+                                       Default: ticket.IN_PROGRESS,
+                               }, task.Status)
+                       }
+
                        results := make([]interface{}, 0)
                        results = append(results, task)
                        return results, nil


Reply via email to