You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@devlake.apache.org by wa...@apache.org on 2022/11/21 03:03:14 UTC

[incubator-devlake] branch feat-plugin-zentao updated: Fix update progress type (#3767)

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

warren pushed a commit to branch feat-plugin-zentao
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/feat-plugin-zentao by this push:
     new 7671c7649 Fix update progress type (#3767)
7671c7649 is described below

commit 7671c7649f25186970125e0cc7a306b0b1c9cf72
Author: Warren Chen <yi...@merico.dev>
AuthorDate: Mon Nov 21 11:03:10 2022 +0800

    Fix update progress type (#3767)
    
    * fix(jenkins): update build stages logic (#3760)
    
    * feat(dora): update logic for change lead time (#3742)
    
    * feat(dora): update logic for change lead time
    
    closes #3516
    
    * feat(dora): update e2e according to review
    
    * fix(zentao): update progress typoe
---
 models/domainlayer/code/pull_request.go            |  35 ++--
 .../domainlayer/crossdomain/project_pr_metric.go   |  39 +++++
 .../20221111_add_project_pr_metric.go              |  63 +++++++
 .../archived/project_pr_metrics.go                 |  35 ++++
 models/migrationscripts/register.go                |   1 +
 .../dora/e2e/calculate_change_lead_time_test.go    |  61 ++-----
 .../dora/e2e/raw_tables/cicd_pipeline_commits.csv  |  17 ++
 plugins/dora/e2e/raw_tables/cicd_scopes.csv        |   4 +
 .../e2e/raw_tables/cicd_tasks_changeleadtime.csv   |  19 +++
 plugins/dora/e2e/raw_tables/commits_diffs.csv      |  13 ++
 plugins/dora/e2e/raw_tables/project_mapping.csv    |   6 +
 plugins/dora/e2e/raw_tables/pull_requests.csv      |  22 +--
 plugins/dora/e2e/raw_tables/repos.csv              |   7 +-
 .../e2e/snapshot_tables/project_pr_metrics.csv     |   7 +
 plugins/dora/e2e/snapshot_tables/pull_requests.csv |  10 --
 plugins/dora/tasks/change_lead_time_calculator.go  | 183 ++++++++++++---------
 plugins/dora/tasks/task_data.go                    |   2 +
 plugins/jenkins/e2e/builds_test.go                 |  23 ++-
 .../e2e/raw_tables/_raw_jenkins_api_jobs.csv       |   4 +-
 .../e2e/raw_tables/_raw_jenkins_api_stages.csv     |   3 +
 .../raw_tables/_tool_jenkins_builds_for_stages.csv |   2 +
 .../e2e/raw_tables/_tool_jenkins_stages.csv        |  20 +++
 .../e2e/snapshot_tables/_tool_jenkins_builds.csv   |  60 +++----
 .../_tool_jenkins_builds_after_enrich.csv          |  30 ++++
 plugins/jenkins/e2e/snapshot_tables/cicd_tasks.csv |   4 -
 plugins/jenkins/tasks/build_stages_enricher.go     |  43 ++---
 .../e2e/raw_tables/_raw_zentao_api_executions.csv  |   2 +-
 .../e2e/raw_tables/_raw_zentao_api_products.csv    |   2 +-
 .../e2e/raw_tables/_raw_zentao_api_tasks.csv       |   6 +-
 .../e2e/snapshot_tables/_tool_zentao_bugs.csv      |   2 +-
 .../snapshot_tables/_tool_zentao_executions.csv    |   2 +-
 .../e2e/snapshot_tables/_tool_zentao_products.csv  |   2 +-
 .../e2e/snapshot_tables/_tool_zentao_tasks.csv     |   8 +-
 plugins/zentao/models/archived/bug.go              |   2 +-
 plugins/zentao/models/archived/execution.go        |   4 +-
 plugins/zentao/models/archived/product.go          |   2 +-
 plugins/zentao/models/archived/project.go          |  20 +--
 plugins/zentao/models/archived/task.go             |   4 +-
 plugins/zentao/models/bug.go                       |   4 +-
 plugins/zentao/models/execution.go                 |  10 +-
 plugins/zentao/models/product.go                   |  26 +--
 plugins/zentao/models/project.go                   |  10 +-
 plugins/zentao/models/task.go                      |  16 +-
 plugins/zentao/tasks/bug_extractor.go              |   2 +-
 plugins/zentao/tasks/task_extractor.go             |   2 +-
 45 files changed, 538 insertions(+), 301 deletions(-)

diff --git a/models/domainlayer/code/pull_request.go b/models/domainlayer/code/pull_request.go
index 126884a80..e17e6970d 100644
--- a/models/domainlayer/code/pull_request.go
+++ b/models/domainlayer/code/pull_request.go
@@ -33,28 +33,19 @@ type PullRequest struct {
 	Url         string `gorm:"type:varchar(255)"`
 	AuthorName  string `gorm:"type:varchar(100)"`
 	//User		   domainUser.User `gorm:"foreignKey:AuthorId"`
-	AuthorId           string `gorm:"type:varchar(100)"`
-	ParentPrId         string `gorm:"index;type:varchar(100)"`
-	PullRequestKey     int
-	CreatedDate        time.Time
-	MergedDate         *time.Time
-	ClosedDate         *time.Time
-	Type               string `gorm:"type:varchar(100)"`
-	Component          string `gorm:"type:varchar(100)"`
-	MergeCommitSha     string `gorm:"type:varchar(40)"`
-	HeadRef            string `gorm:"type:varchar(255)"`
-	BaseRef            string `gorm:"type:varchar(255)"`
-	BaseCommitSha      string `gorm:"type:varchar(40)"`
-	HeadCommitSha      string `gorm:"type:varchar(40)"`
-	CodingTimespan     *int64
-	ReviewLag          *int64
-	ReviewTimespan     *int64
-	DeployTimespan     *int64
-	ChangeTimespan     *int64
-	OrigCodingTimespan int64
-	OrigReviewLag      int64
-	OrigReviewTimespan int64
-	OrigDeployTimespan int64
+	AuthorId       string `gorm:"type:varchar(100)"`
+	ParentPrId     string `gorm:"index;type:varchar(100)"`
+	PullRequestKey int
+	CreatedDate    time.Time
+	MergedDate     *time.Time
+	ClosedDate     *time.Time
+	Type           string `gorm:"type:varchar(100)"`
+	Component      string `gorm:"type:varchar(100)"`
+	MergeCommitSha string `gorm:"type:varchar(40)"`
+	HeadRef        string `gorm:"type:varchar(255)"`
+	BaseRef        string `gorm:"type:varchar(255)"`
+	BaseCommitSha  string `gorm:"type:varchar(40)"`
+	HeadCommitSha  string `gorm:"type:varchar(40)"`
 }
 
 func (PullRequest) TableName() string {
diff --git a/models/domainlayer/crossdomain/project_pr_metric.go b/models/domainlayer/crossdomain/project_pr_metric.go
new file mode 100644
index 000000000..f6c43f22b
--- /dev/null
+++ b/models/domainlayer/crossdomain/project_pr_metric.go
@@ -0,0 +1,39 @@
+/*
+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 crossdomain
+
+import (
+	"github.com/apache/incubator-devlake/models/domainlayer"
+)
+
+type ProjectPrMetric struct {
+	domainlayer.DomainEntity
+	ProjectName    string `gorm:"primaryKey;type:varchar(100)"`
+	FirstCommitSha string
+	CodingTimespan *int64
+	FirstReviewId  string
+	ReviewLag      *int64
+	ReviewTimespan *int64
+	DeploymentId   string
+	DeployTimespan *int64
+	ChangeTimespan *int64
+}
+
+func (ProjectPrMetric) TableName() string {
+	return "project_pr_metrics"
+}
diff --git a/models/migrationscripts/20221111_add_project_pr_metric.go b/models/migrationscripts/20221111_add_project_pr_metric.go
new file mode 100644
index 000000000..68f5782e0
--- /dev/null
+++ b/models/migrationscripts/20221111_add_project_pr_metric.go
@@ -0,0 +1,63 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package migrationscripts
+
+import (
+	"github.com/apache/incubator-devlake/errors"
+	"github.com/apache/incubator-devlake/helpers/migrationhelper"
+	"github.com/apache/incubator-devlake/models/migrationscripts/archived"
+	"github.com/apache/incubator-devlake/plugins/core"
+)
+
+type addProjectPrMetric struct{}
+
+func (u *addProjectPrMetric) Up(baseRes core.BasicRes) errors.Error {
+	db := baseRes.GetDal()
+	err := migrationhelper.AutoMigrateTables(
+		baseRes,
+		&archived.ProjectPrMetric{},
+	)
+	if err != nil {
+		return err
+	}
+	prColums := []string{
+		`coding_timespan`,
+		`review_lag`,
+		`review_timespan`,
+		`deploy_timespan`,
+		`change_timespan`,
+		`orig_coding_timespan`,
+		`orig_review_lag`,
+		`orig_review_timespan`,
+		`orig_deploy_timespan`,
+	}
+	err = db.DropColumns(`pull_requests`, prColums...)
+	if err != nil {
+		return err
+	}
+	err = db.DropColumns(`cicd_pipeline_commits`, "repo_url")
+	return err
+}
+
+func (*addProjectPrMetric) Version() uint64 {
+	return 20221111000001
+}
+
+func (*addProjectPrMetric) Name() string {
+	return "add project pr metric tables"
+}
diff --git a/models/migrationscripts/archived/project_pr_metrics.go b/models/migrationscripts/archived/project_pr_metrics.go
new file mode 100644
index 000000000..03e0f5252
--- /dev/null
+++ b/models/migrationscripts/archived/project_pr_metrics.go
@@ -0,0 +1,35 @@
+/*
+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
+
+type ProjectPrMetric struct {
+	DomainEntity
+	ProjectName    string `gorm:"primaryKey;type:varchar(100)"`
+	FirstReviewId  string
+	FirstCommitSha string
+	CodingTimespan *int64
+	ReviewLag      *int64
+	ReviewTimespan *int64
+	DeploymentId   string
+	DeployTimespan *int64
+	ChangeTimespan *int64
+}
+
+func (ProjectPrMetric) TableName() string {
+	return "project_pr_metrics"
+}
diff --git a/models/migrationscripts/register.go b/models/migrationscripts/register.go
index 7e62d2a51..e28c33174 100644
--- a/models/migrationscripts/register.go
+++ b/models/migrationscripts/register.go
@@ -56,6 +56,7 @@ func All() []core.MigrationScript {
 		new(addCicdScope),
 		new(addSkipOnFail),
 		new(modifyCommitsDiffs),
+		new(addProjectPrMetric),
 		new(addProjectTables),
 		new(addProjectToBluePrint),
 	}
diff --git a/plugins/dora/e2e/calculate_change_lead_time_test.go b/plugins/dora/e2e/calculate_change_lead_time_test.go
index 599d8c483..9667a5040 100644
--- a/plugins/dora/e2e/calculate_change_lead_time_test.go
+++ b/plugins/dora/e2e/calculate_change_lead_time_test.go
@@ -18,13 +18,14 @@ limitations under the License.
 package e2e
 
 import (
-	"testing"
-
 	"github.com/apache/incubator-devlake/helpers/e2ehelper"
+	"github.com/apache/incubator-devlake/models/common"
 	"github.com/apache/incubator-devlake/models/domainlayer/code"
+	"github.com/apache/incubator-devlake/models/domainlayer/crossdomain"
 	"github.com/apache/incubator-devlake/models/domainlayer/devops"
 	"github.com/apache/incubator-devlake/plugins/dora/impl"
 	"github.com/apache/incubator-devlake/plugins/dora/tasks"
+	"testing"
 )
 
 func TestCalculateCLTimeDataFlow(t *testing.T) {
@@ -33,7 +34,7 @@ func TestCalculateCLTimeDataFlow(t *testing.T) {
 
 	taskData := &tasks.DoraTaskData{
 		Options: &tasks.DoraOptions{
-			RepoId: "github:GithubRepo:1:384111310",
+			ProjectName: "project1",
 			TransformationRules: tasks.TransformationRules{
 				ProductionPattern: "(?i)deploy",
 			},
@@ -43,52 +44,22 @@ func TestCalculateCLTimeDataFlow(t *testing.T) {
 	dataflowTester.FlushTabler(&code.PullRequest{})
 
 	// import raw data table
-	dataflowTester.ImportCsvIntoTabler("./raw_tables/lake_cicd_tasks.csv", &devops.CICDTask{})
+	dataflowTester.ImportCsvIntoTabler("./raw_tables/cicd_tasks_changeleadtime.csv", &devops.CICDTask{})
 	dataflowTester.ImportCsvIntoTabler("./raw_tables/pull_requests.csv", &code.PullRequest{})
+	dataflowTester.ImportCsvIntoTabler("./raw_tables/commits_diffs.csv", &code.CommitsDiff{})
+	dataflowTester.ImportCsvIntoTabler("./raw_tables/cicd_pipeline_commits.csv", &devops.CiCDPipelineCommit{})
+	dataflowTester.ImportCsvIntoTabler("./raw_tables/project_mapping.csv", &crossdomain.ProjectMapping{})
+	dataflowTester.ImportCsvIntoTabler("./raw_tables/commits.csv", &code.Commit{})
 	dataflowTester.ImportCsvIntoTabler("./raw_tables/pull_request_comments.csv", &code.PullRequestComment{})
 	dataflowTester.ImportCsvIntoTabler("./raw_tables/pull_request_commits.csv", &code.PullRequestCommit{})
-	dataflowTester.ImportCsvIntoTabler("./raw_tables/commits.csv", &code.Commit{})
+	dataflowTester.ImportCsvIntoTabler("./raw_tables/repos.csv", &code.Repo{})
+	dataflowTester.ImportCsvIntoTabler("./raw_tables/cicd_scopes.csv", &devops.CicdScope{})
 
 	// verify converter
+	dataflowTester.FlushTabler(&crossdomain.ProjectPrMetric{})
 	dataflowTester.Subtask(tasks.CalculateChangeLeadTimeMeta, taskData)
-	dataflowTester.VerifyTable(
-		code.PullRequest{},
-		"./snapshot_tables/pull_requests.csv",
-		[]string{
-			"id",
-			"_raw_data_params",
-			"_raw_data_table",
-			"_raw_data_id",
-			"_raw_data_remark",
-			"base_repo_id",
-			"head_repo_id",
-			"status",
-			"title",
-			"description",
-			"url",
-			"author_name",
-			"author_id",
-			"parent_pr_id",
-			"pull_request_key",
-			"created_date",
-			"merged_date",
-			"closed_date",
-			"type",
-			"component",
-			"merge_commit_sha",
-			"head_ref",
-			"base_ref",
-			"base_commit_sha",
-			"head_commit_sha",
-			"coding_timespan",
-			"review_lag",
-			"review_timespan",
-			"deploy_timespan",
-			"change_timespan",
-			"orig_coding_timespan",
-			"orig_review_lag",
-			"orig_review_timespan",
-			"orig_deploy_timespan",
-		},
-	)
+	dataflowTester.VerifyTableWithOptions(&crossdomain.ProjectPrMetric{}, e2ehelper.TableOptions{
+		CSVRelPath:  "./snapshot_tables/project_pr_metrics.csv",
+		IgnoreTypes: []interface{}{common.NoPKModel{}},
+	})
 }
diff --git a/plugins/dora/e2e/raw_tables/cicd_pipeline_commits.csv b/plugins/dora/e2e/raw_tables/cicd_pipeline_commits.csv
new file mode 100644
index 000000000..3c5029696
--- /dev/null
+++ b/plugins/dora/e2e/raw_tables/cicd_pipeline_commits.csv
@@ -0,0 +1,17 @@
+pipeline_id,commit_sha,branch,repo_id,repo_url
+pipeline110,commit300,z5z07j8qDO,repo1,
+pipeline111,commit301,7j3t5IFWb6,repo1,
+pipeline112,commit302,VBSqoF8WIh,repo1,
+pipeline113,commit303,VXyuBwhkIE,repo1,
+pipeline114,commit304,hJlTujPgFq,repo1,
+pipeline115,commit305,I8UmFLEYPO,repo2,
+pipeline116,commit306,oMhYUwGyVX,repo1,
+pipeline117,commit307,SQGBUB075n,repo1,
+pipeline118,commit308,XVxzMFClc5,repo1,
+pipeline119,commit309,Pr9qLgnNPD,repo1,
+pipeline18,commit22,QXxXzo0V6U,repo2,
+pipeline19,commit22,ey6y4W9jdm,repo3,
+pipeline19,commit24,wUYEULZ850,repo2,
+pipeline20,commit31,ptVxHa6jwh,repo1,
+pipeline21,commit32,RcX46MDXRQ,repo1,
+pipeline24,commit22,8LaxgnEhid,repo2,
diff --git a/plugins/dora/e2e/raw_tables/cicd_scopes.csv b/plugins/dora/e2e/raw_tables/cicd_scopes.csv
new file mode 100644
index 000000000..29cbc88d2
--- /dev/null
+++ b/plugins/dora/e2e/raw_tables/cicd_scopes.csv
@@ -0,0 +1,4 @@
+id
+cicd1
+cicd2
+cicd3
diff --git a/plugins/dora/e2e/raw_tables/cicd_tasks_changeleadtime.csv b/plugins/dora/e2e/raw_tables/cicd_tasks_changeleadtime.csv
new file mode 100644
index 000000000..b8acabbae
--- /dev/null
+++ b/plugins/dora/e2e/raw_tables/cicd_tasks_changeleadtime.csv
@@ -0,0 +1,19 @@
+id,name,pipeline_id,status,result,type,environment,duration_sec,started_date,finished_date,cicd_scope_id
+task10,deployxIG,pipeline110,DONE,SUCCESS,DEPLOYMENT,PRODUCTION,,2022-07-19 22:06:28,2022-11-13 22:37:21,cicd1
+task11,deploya,pipeline111,DONE,SUCCESS,DEPLOYMENT,PRODUCTION,,2022-08-06 14:06:50,2022-11-13 00:07:21,cicd1
+task12,deployc,pipeline112,DONE,SUCCESS,DEPLOYMENT,PRODUCTION,,2022-08-23 17:44:05,2022-11-02 07:21:09,cicd2
+task13,deploy,pipeline113,DONE,SUCCESS,DEPLOYMENT,PRODUCTION,,2022-08-30 23:45:29,2022-11-28 00:46:47,cicd1
+task14,deployp0;,pipeline114,DONE,SUCCESS,DEPLOYMENT,PRODUCTION,,2022-09-07 02:49:26,2022-11-16 20:34:01,cicd1
+task15,deployY{,pipeline115,DONE,SUCCESS,DEPLOYMENT,PRODUCTION,,2022-09-27 01:07:50,2022-11-19 07:17:33,cicd2
+task16,deploy8',pipeline116,DONE,SUCCESS,DEPLOYMENT,PRODUCTION,,2022-09-30 21:05:38,2022-11-08 07:56:03,cicd1
+task17,deployKd%,pipeline117,IN_PROGRESS,,DEPLOYMENT,PRODUCTION,,2022-10-09 06:42:02,,cicd1
+task19,deploy1,pipeline119,DONE,FAILURE,DEPLOYMENT,PRODUCTION,,2022-10-24 18:41:04,2022-11-24 04:26:48,cicd1
+task21,deploy^^.,pipeline39,DONE,FAILURE,DEPLOYMENT,STAGING,,2004-01-10 03:31:11,2022-11-28 20:41:59,cicd1
+task22,deploy,pipeline35,DONE,SUCCESS,DEPLOYMENT,TESTING,,2000-10-25 09:57:28,2022-11-28 21:24:02,cicd1
+task23,deploy,pipeline36,IN_PROGRESS,,DEPLOYMENT,TESTING,,2005-02-07 11:03:27,2022-11-05 18:18:03,cicd1
+task24,deploym,pipeline12,IN_PROGRESS,,DEPLOYMENT,STAGING,,2015-08-11 19:58:06,2022-11-01 22:31:56,cicd1
+task25,deploy$p<,pipeline26,DONE,FAILURE,,TESTING,,2014-02-06 13:42:43,2022-11-30 08:01:38,cicd3
+task26,deployb>@,pipeline20,IN_PROGRESS,,DEPLOYMENT,STAGING,,2016-08-26 05:41:49,2022-11-15 07:31:46,cicd2
+task27,deployKfn,pipeline37,DONE,SUCCESS,DEPLOYMENT,STAGING,,2003-12-13 23:19:14,2022-11-11 18:29:31,cicd2
+task28,deployl?,pipeline29,IN_PROGRESS,,DEPLOYMENT,TESTING,,2007-01-19 01:13:39,2022-11-24 05:39:46,cicd3
+task29,deployUb,pipeline27,IN_PROGRESS,,DEPLOYMENT,TESTING,,2006-05-20 18:17:13,2022-11-28 10:13:51,cicd2
diff --git a/plugins/dora/e2e/raw_tables/commits_diffs.csv b/plugins/dora/e2e/raw_tables/commits_diffs.csv
new file mode 100644
index 000000000..f81ed28c3
--- /dev/null
+++ b/plugins/dora/e2e/raw_tables/commits_diffs.csv
@@ -0,0 +1,13 @@
+commit_sha,new_commit_sha,old_commit_sha,sorting_index
+commit200,commit301,commit300,438
+commit201,commit302,commit301,491
+commit202,commit303,commit302,808
+commit203,commit304,commit303,247
+commit204,commit305,commit304,247
+commit3,commit24,commit25,58
+commit4,commit24,commit22,802
+commit5,commit23,commit23,355
+commit6,commit25,commit21,908
+commit7,commit27,commit23,47
+commit8,commit24,commit23,51
+commit9,commit22,commit20,957
diff --git a/plugins/dora/e2e/raw_tables/project_mapping.csv b/plugins/dora/e2e/raw_tables/project_mapping.csv
new file mode 100644
index 000000000..bcc085fc9
--- /dev/null
+++ b/plugins/dora/e2e/raw_tables/project_mapping.csv
@@ -0,0 +1,6 @@
+project_name,table,row_id
+project1,cicd_scopes,cicd1
+project1,cicd_scopes,cicd2
+project1,repos,repo1
+project1,repos,repo2
+project2,cicd_scopes,cicd3
diff --git a/plugins/dora/e2e/raw_tables/pull_requests.csv b/plugins/dora/e2e/raw_tables/pull_requests.csv
index b9c1a6684..e32c680a1 100644
--- a/plugins/dora/e2e/raw_tables/pull_requests.csv
+++ b/plugins/dora/e2e/raw_tables/pull_requests.csv
@@ -1,10 +1,12 @@
-id,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,base_repo_id,head_repo_id,status,title,description,url,author_name,author_id,parent_pr_id,pull_request_key,created_date,merged_date,closed_date,type,component,merge_commit_sha,head_ref,base_ref,base_commit_sha,head_commit_sha,coding_timespan,review_lag,review_timespan,deploy_timespan,change_timespan,orig_coding_timespan,orig_review_lag,orig_review_timespan,orig_deploy_timespan
-github:GithubPullRequest:1:1043463302,2022-09-15 05:36:39.856,2022-09-15 05:36:39.856,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,13176,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,feat: implement API for plugin `customize`,"""# Summary\r\n\r\nfulfill the requirement \r\n#2880 ([Feature][customize] implement API for plugin customize) \r\n#2985 ([Feature][customize] new sub-task ExtractCustomizedFields for [...]
-github:GithubPullRequest:1:1048233599,2022-09-15 05:36:39.856,2022-09-15 05:36:39.856,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,13211,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,feat: new plugin for gitea,"""# Summary\r\n[Gitea](https://gitea.io/) is an open-source software package for hosting software development version control using Git as well as other collaborative features like bug tracking, code [...]
-github:GithubPullRequest:1:1049191985,2022-09-15 05:36:40.170,2022-09-15 05:36:40.170,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12680,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,[issue-2908]: Bump lake-builder version to 0.0.8,"""### ⚠️ Pre Checklist\r\n\r\n> Please complete _ALL_ items in this checklist, and remove before submitting\r\n\r\n- [x] I have read through the [Contributing Documentation](htt [...]
-github:GithubPullRequest:1:1051243958,2022-09-15 05:36:40.170,2022-09-15 05:36:40.170,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12691,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix: golangci-lint error,"""# Summary\r\n\r\ngolint-ci was down for a while, this PR try to fix all missed linting errors during the period.\r\n\r\n### Screenshots\r\n![image](https://user-images.githubusercontent.com/61080/189 [...]
-github:GithubPullRequest:1:1051273681,2022-09-15 05:36:40.170,2022-09-15 05:36:40.170,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12693,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix: update pipelineId,"""# Summary\r\n\r\nupdate pipelineId\r\n\r\n### Does this close any open issues?\r\nrelated to #2998 \r\n\r\n### Screenshots\r\n![image](https://user-images.githubusercontent.com/101256042/189307562-88fc [...]
-github:GithubPullRequest:1:1051340471,2022-09-15 05:36:40.170,2022-09-15 05:36:40.170,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12694,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,Jenkins fix,"""# Summary\r\nAdd TestJenkinsStagesDataFlow.\r\nAdd path to stages collect.\r\n\r\n<!--\r\nThanks for submitting a pull request!\r\n\r\nWe appreciate you spending the time to work on these changes.\r\nPlease fill  [...]
-github:GithubPullRequest:1:1051524882,2022-09-15 05:36:40.170,2022-09-15 05:36:40.170,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12696,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix: task failure caused by 404,"""# Summary\r\nfix #2960 [Bug][gitihub] collect account failed by not found user\r\nIn the case of `err == ErrIgnoreAndContinue`, the `err` should not be wrapped, because on the caller side, the [...]
-github:GithubPullRequest:1:1051574863,2022-09-15 05:36:40.170,2022-09-15 05:36:40.170,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12697,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix(jenkins): update e2e,"""# Summary\r\n\r\nUpdate e2e according to recent changes\r\n\r\n### Does this close any open issues?\r\nrelate to #2854\r\n\r\n\r\n### Screenshots\r\nInclude any relevant screenshots here.\r\n\r\n###  [...]
-github:GithubPullRequest:1:1051637383,2022-09-15 05:36:40.170,2022-09-15 05:36:40.170,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12699,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix(gitlab): update e2e,"""# Summary\r\n\r\nupdate gitlab e2e according to recent changes\r\n\r\n### Does this close any open issues?\r\nrelate to #2871\r\n\r\n\r\n### Screenshots\r\nInclude any relevant screenshots here.\r\n\r [...]
+id,_raw_data_params,base_repo_id,created_date,merged_date,merge_commit_sha,base_commit_sha,head_commit_sha
+github:GithubPullRequest:1:1043463302,,repo1,2022-09-01 09:34:40,2022-09-09 07:52:53,commit200,,
+github:GithubPullRequest:1:1048233599,,repo1,2022-09-07 04:07:39,2022-09-10 02:35:43,commit201,,
+github:GithubPullRequest:1:1049191985,,repo1,2022-09-07 20:30:44,2022-09-09 03:39:50,commit202,,
+github:GithubPullRequest:1:1051112182,,repo1,2022-09-09 12:35:29,2022-09-09 13:32:51,commit203,,
+github:GithubPullRequest:1:1051524992,,repo1,2022-09-09 12:35:29,,commit111,,
+github:GithubPullRequest:1:1051524993,,repo1,2022-09-09 12:35:29,,commit112,,
+github:GithubPullRequest:1:1051524994,,repo1,2022-09-09 12:35:29,,commit113,,
+github:GithubPullRequest:1:1051574863,,repo1,2022-09-09 13:23:37,2022-09-09 15:12:23,commit204,,
+github:GithubPullRequest:1:1051637383,,repo1,2022-09-09 14:17:24,2022-09-09 15:16:28,commit92,,
+github:GithubPullRequest:2:1051123483,,repo4,2022-09-09 14:17:24,2022-09-09 15:16:28,commit109,,
+github:GithubPullRequest:3:1051342463,,repo3,2022-09-09 13:23:37,2022-09-09 15:12:23,commit102,,
diff --git a/plugins/dora/e2e/raw_tables/repos.csv b/plugins/dora/e2e/raw_tables/repos.csv
index 199b4f355..6cf992dec 100644
--- a/plugins/dora/e2e/raw_tables/repos.csv
+++ b/plugins/dora/e2e/raw_tables/repos.csv
@@ -1,2 +1,5 @@
-id,created_at,updated_at,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,name,url,description,owner_id,language,forked_from,created_date,updated_date,deleted
-github:GithubRepo:1:384111310,2022-09-20 13:47:48.519,2022-09-20 13:47:48.519,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_repositories,1,"",apache/incubator-devlake,https://github.com/apache/incubator-devlake,"Apache DevLake is an open-source dev data platform to ingest, analyze, and visualize the fragmented data from DevOps tools, which can distill insights to improve engineering productivity.","",Go,"",2021-07-08 12:06:09,2022-09-20 03:25:21,0
+id
+github:GithubRepo:1:384111310
+repo1
+repo2
+repo3
diff --git a/plugins/dora/e2e/snapshot_tables/project_pr_metrics.csv b/plugins/dora/e2e/snapshot_tables/project_pr_metrics.csv
new file mode 100644
index 000000000..9391c7c40
--- /dev/null
+++ b/plugins/dora/e2e/snapshot_tables/project_pr_metrics.csv
@@ -0,0 +1,7 @@
+id,project_name,first_commit_sha,coding_timespan,first_review_id,review_lag,review_timespan,deployment_id,deploy_timespan,change_timespan
+github:GithubPullRequest:1:1043463302,project1,75ab753225b5b8acf3bc6e40e463b54b6800e7ed,,github:GithubPrReview:1:1098724785,8558,2859,task11,93134,104551
+github:GithubPullRequest:1:1048233599,project1,4f8cdefc9a9d53af16dd482c61623312eb9e9b5e,,github:GithubPrReview:1:0,194,4033,task12,76605,80832
+github:GithubPullRequest:1:1049191985,project1,4b71faf666833c0c7b915a512811e2c5e746d3de,1,github:GithubPrReview:1:1099918590,156,1712,task13,115026,116895
+github:GithubPullRequest:1:1051112182,project1,,,,,,task14,98341,98341
+github:GithubPullRequest:1:1051574863,project1,,,,,,,,
+github:GithubPullRequest:1:1051637383,project1,9d53fb594958e65456793caa1bfa8d07a7614291,1,,45,13,,,59
diff --git a/plugins/dora/e2e/snapshot_tables/pull_requests.csv b/plugins/dora/e2e/snapshot_tables/pull_requests.csv
deleted file mode 100644
index cab46ef5a..000000000
--- a/plugins/dora/e2e/snapshot_tables/pull_requests.csv
+++ /dev/null
@@ -1,10 +0,0 @@
-id,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark,base_repo_id,head_repo_id,status,title,description,url,author_name,author_id,parent_pr_id,pull_request_key,created_date,merged_date,closed_date,type,component,merge_commit_sha,head_ref,base_ref,base_commit_sha,head_commit_sha,coding_timespan,review_lag,review_timespan,deploy_timespan,change_timespan,orig_coding_timespan,orig_review_lag,orig_review_timespan,orig_deploy_timespan
-github:GithubPullRequest:1:1043463302,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,13176,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,feat: implement API for plugin `customize`,"""# Summary\r\n\r\nfulfill the requirement \r\n#2880 ([Feature][customize] implement API for plugin customize) \r\n#2985 ([Feature][customize] new sub-task ExtractCustomizedFields for plugin customize)\r\n\r\nrelated to #2802 \r\nW [...]
-github:GithubPullRequest:1:1048233599,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,13211,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,feat: new plugin for gitea,"""# Summary\r\n[Gitea](https://gitea.io/) is an open-source software package for hosting software development version control using Git as well as other collaborative features like bug tracking, code review, kanban boards, tickets, and wikis.\r\n< [...]
-github:GithubPullRequest:1:1049191985,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12680,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,[issue-2908]: Bump lake-builder version to 0.0.8,"""### ⚠️ Pre Checklist\r\n\r\n> Please complete _ALL_ items in this checklist, and remove before submitting\r\n\r\n- [x] I have read through the [Contributing Documentation](https://devlake.apache.org/community/).\r\n- [x] I  [...]
-github:GithubPullRequest:1:1051243958,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12691,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix: golangci-lint error,"""# Summary\r\n\r\ngolint-ci was down for a while, this PR try to fix all missed linting errors during the period.\r\n\r\n### Screenshots\r\n![image](https://user-images.githubusercontent.com/61080/189302830-d54ad54d-a3e6-470c-b517-9d803dbcd248.png) [...]
-github:GithubPullRequest:1:1051273681,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12693,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix: update pipelineId,"""# Summary\r\n\r\nupdate pipelineId\r\n\r\n### Does this close any open issues?\r\nrelated to #2998 \r\n\r\n### Screenshots\r\n![image](https://user-images.githubusercontent.com/101256042/189307562-88fc6b6d-5daf-4b9c-8cec-0493cfe5a42c.png)\r\n\r\n### [...]
-github:GithubPullRequest:1:1051340471,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12694,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,Jenkins fix,"""# Summary\r\nAdd TestJenkinsStagesDataFlow.\r\nAdd path to stages collect.\r\n\r\n<!--\r\nThanks for submitting a pull request!\r\n\r\nWe appreciate you spending the time to work on these changes.\r\nPlease fill out as many sections below as possible.\r\n-->\r [...]
-github:GithubPullRequest:1:1051524882,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12696,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix: task failure caused by 404,"""# Summary\r\nfix #2960 [Bug][gitihub] collect account failed by not found user\r\nIn the case of `err == ErrIgnoreAndContinue`, the `err` should not be wrapped, because on the caller side, the expression `err == ErrIgnoreAndContinue`  would [...]
-github:GithubPullRequest:1:1051574863,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12697,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix(jenkins): update e2e,"""# Summary\r\n\r\nUpdate e2e according to recent changes\r\n\r\n### Does this close any open issues?\r\nrelate to #2854\r\n\r\n\r\n### Screenshots\r\nInclude any relevant screenshots here.\r\n\r\n### Other Information\r\nAny other information that  [...]
-github:GithubPullRequest:1:1051637383,"{""ConnectionId"":1,""Owner"":""apache"",""Repo"":""incubator-devlake""}",_raw_github_api_pull_requests,12699,,github:GithubRepo:1:384111310,github:GithubRepo:1:384111310,closed,fix(gitlab): update e2e,"""# Summary\r\n\r\nupdate gitlab e2e according to recent changes\r\n\r\n### Does this close any open issues?\r\nrelate to #2871\r\n\r\n\r\n### Screenshots\r\nInclude any relevant screenshots here.\r\n\r\n### Other Information\r\nAny other information [...]
diff --git a/plugins/dora/tasks/change_lead_time_calculator.go b/plugins/dora/tasks/change_lead_time_calculator.go
index d6bcdb4b9..33e80f5da 100644
--- a/plugins/dora/tasks/change_lead_time_calculator.go
+++ b/plugins/dora/tasks/change_lead_time_calculator.go
@@ -19,12 +19,12 @@ package tasks
 
 import (
 	goerror "errors"
+	"github.com/apache/incubator-devlake/models/domainlayer/crossdomain"
 	"reflect"
 	"time"
 
 	"github.com/apache/incubator-devlake/errors"
 	"github.com/apache/incubator-devlake/models/domainlayer/code"
-	"github.com/apache/incubator-devlake/models/domainlayer/devops"
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/core/dal"
 	"github.com/apache/incubator-devlake/plugins/helper"
@@ -34,9 +34,41 @@ import (
 func CalculateChangeLeadTime(taskCtx core.SubTaskContext) errors.Error {
 	db := taskCtx.GetDal()
 	log := taskCtx.GetLogger()
+	data := taskCtx.GetData().(*DoraTaskData)
+	// construct a list of tuple[task, oldPipelineCommitSha, newPipelineCommitSha, taskFinishedDate]
+	pipelineIdClauses := []dal.Clause{
+		dal.Select(`ct.id as task_id, cpc.commit_sha as new_deploy_commit_sha, 
+			ct.finished_date as task_finished_date, cpc.repo_id as repo_id`),
+		dal.From(`cicd_tasks ct`),
+		dal.Join(`left join cicd_pipeline_commits cpc on ct.pipeline_id = cpc.pipeline_id`),
+		dal.Join(`left join project_mapping pm on pm.row_id = ct.cicd_scope_id`),
+		dal.Where(`ct.environment = ? and ct.type = ? and ct.result = ? and pm.project_name = ? and pm.table = ?`,
+			"PRODUCTION", "DEPLOYMENT", "SUCCESS", data.Options.ProjectName, "cicd_scopes"),
+		dal.Orderby(`cpc.repo_id, ct.started_date `),
+	}
+	deploymentPairList := make([]deploymentPair, 0)
+	err := db.All(&deploymentPairList, pipelineIdClauses...)
+	if err != nil {
+		return err
+	}
+	// deploymentPairList[i-1].NewDeployCommitSha is deploymentPairList[i].OldDeployCommitSha
+	oldDeployCommitSha := ""
+	lastRepoId := ""
+	for i := 0; i < len(deploymentPairList); i++ {
+		// if two deployments belong to different repo, let's skip
+		if lastRepoId == deploymentPairList[i].RepoId {
+			deploymentPairList[i].OldDeployCommitSha = oldDeployCommitSha
+		} else {
+			lastRepoId = deploymentPairList[i].RepoId
+		}
+		oldDeployCommitSha = deploymentPairList[i].NewDeployCommitSha
+	}
+
+	// get prs by repo project_name
 	clauses := []dal.Clause{
 		dal.From(&code.PullRequest{}),
-		dal.Where("merged_date IS NOT NULL"),
+		dal.Join(`left join project_mapping pm on pm.row_id = pull_requests.base_repo_id`),
+		dal.Where("pull_requests.merged_date IS NOT NULL and pm.project_name = ? and pm.table = ?", data.Options.ProjectName, "repos"),
 	}
 	cursor, err := db.Cursor(clauses...)
 	if err != nil {
@@ -44,11 +76,11 @@ func CalculateChangeLeadTime(taskCtx core.SubTaskContext) errors.Error {
 	}
 	defer cursor.Close()
 
-	enricher, err := helper.NewDataConverter(helper.DataConverterArgs{
+	converter, err := helper.NewDataConverter(helper.DataConverterArgs{
 		RawDataSubTaskArgs: helper.RawDataSubTaskArgs{
-			Ctx:    taskCtx,
+			Ctx: taskCtx,
 			Params: DoraApiParams{
-				// TODO
+				ProjectName: data.Options.ProjectName,
 			},
 			Table: "pull_requests",
 		},
@@ -57,66 +89,74 @@ func CalculateChangeLeadTime(taskCtx core.SubTaskContext) errors.Error {
 		Input:        cursor,
 		Convert: func(inputRow interface{}) ([]interface{}, errors.Error) {
 			pr := inputRow.(*code.PullRequest)
-			firstCommitDate, err := getFirstCommitTime(pr.Id, db)
+			firstCommit, err := getFirstCommit(pr.Id, db)
+			if err != nil {
+				return nil, err
+			}
+			projectPrMetric := &crossdomain.ProjectPrMetric{}
+			projectPrMetric.Id = pr.Id
+			projectPrMetric.ProjectName = data.Options.ProjectName
 			if err != nil {
 				return nil, err
 			}
-			if firstCommitDate != nil {
-				codingTime := int64(pr.CreatedDate.Sub(*firstCommitDate).Seconds())
+			if firstCommit != nil {
+				codingTime := int64(pr.CreatedDate.Sub(firstCommit.AuthoredDate).Seconds())
 				if codingTime/60 == 0 && codingTime%60 > 0 {
 					codingTime = 1
 				} else {
 					codingTime = codingTime / 60
 				}
-				pr.OrigCodingTimespan = codingTime
+				projectPrMetric.CodingTimespan = processNegativeValue(codingTime)
+				projectPrMetric.FirstCommitSha = firstCommit.Sha
 			}
-			firstReviewTime, err := getFirstReviewTime(pr.Id, pr.AuthorId, db)
+			firstReview, err := getFirstReview(pr.Id, pr.AuthorId, db)
 			if err != nil {
 				return nil, err
 			}
-			if firstReviewTime != nil {
-				pr.OrigReviewLag = int64(firstReviewTime.Sub(pr.CreatedDate).Minutes())
-				pr.OrigReviewTimespan = int64(pr.MergedDate.Sub(*firstReviewTime).Minutes())
+			if firstReview != nil {
+				projectPrMetric.ReviewLag = processNegativeValue(int64(firstReview.CreatedDate.Sub(pr.CreatedDate).Minutes()))
+				projectPrMetric.ReviewTimespan = processNegativeValue(int64(pr.MergedDate.Sub(firstReview.CreatedDate).Minutes()))
+				projectPrMetric.FirstReviewId = firstReview.ReviewId
 			}
-			deployment, err := getDeployment(devops.PRODUCTION, *pr.MergedDate, db)
+			deployment, err := getDeployment(pr.MergeCommitSha, pr.BaseRepoId, deploymentPairList, db)
 			if err != nil {
 				return nil, err
 			}
-			if deployment != nil && deployment.FinishedDate != nil {
-				timespan := deployment.FinishedDate.Sub(*pr.MergedDate)
-				pr.OrigDeployTimespan = int64(timespan.Minutes())
+			if deployment != nil && deployment.TaskFinishedDate != nil {
+				timespan := deployment.TaskFinishedDate.Sub(*pr.MergedDate)
+				projectPrMetric.DeployTimespan = processNegativeValue(int64(timespan.Minutes()))
+				projectPrMetric.DeploymentId = deployment.TaskId
 			} else {
 				log.Debug("deploy time of pr %v is nil\n", pr.PullRequestKey)
 			}
-			processNegativeValue(pr)
-			pr.ChangeTimespan = nil
-			result := int64(0)
-			if pr.CodingTimespan != nil {
-				result += *pr.CodingTimespan
+			projectPrMetric.ChangeTimespan = nil
+			var result int64
+			if projectPrMetric.CodingTimespan != nil {
+				result += *projectPrMetric.CodingTimespan
 			}
-			if pr.ReviewLag != nil {
-				result += *pr.ReviewLag
+			if projectPrMetric.ReviewLag != nil {
+				result += *projectPrMetric.ReviewLag
 			}
-			if pr.ReviewTimespan != nil {
-				result += *pr.ReviewTimespan
+			if projectPrMetric.ReviewTimespan != nil {
+				result += *projectPrMetric.ReviewTimespan
 			}
-			if pr.DeployTimespan != nil {
-				result += *pr.DeployTimespan
+			if projectPrMetric.DeployTimespan != nil {
+				result += *projectPrMetric.DeployTimespan
 			}
 			if result > 0 {
-				pr.ChangeTimespan = &result
+				projectPrMetric.ChangeTimespan = &result
 			}
-			return []interface{}{pr}, nil
+			return []interface{}{projectPrMetric}, nil
 		},
 	})
 	if err != nil {
 		return err
 	}
 
-	return enricher.Execute()
+	return converter.Execute()
 }
 
-func getFirstCommitTime(prId string, db dal.Dal) (*time.Time, errors.Error) {
+func getFirstCommit(prId string, db dal.Dal) (*code.Commit, errors.Error) {
 	commit := &code.Commit{}
 	commitClauses := []dal.Clause{
 		dal.From(&code.Commit{}),
@@ -131,10 +171,10 @@ func getFirstCommitTime(prId string, db dal.Dal) (*time.Time, errors.Error) {
 	if err != nil {
 		return nil, err
 	}
-	return &commit.AuthoredDate, nil
+	return commit, nil
 }
 
-func getFirstReviewTime(prId string, prCreator string, db dal.Dal) (*time.Time, errors.Error) {
+func getFirstReview(prId string, prCreator string, db dal.Dal) (*code.PullRequestComment, errors.Error) {
 	review := &code.PullRequestComment{}
 	commentClauses := []dal.Clause{
 		dal.From(&code.PullRequestComment{}),
@@ -148,57 +188,40 @@ func getFirstReviewTime(prId string, prCreator string, db dal.Dal) (*time.Time,
 	if err != nil {
 		return nil, err
 	}
-	return &review.CreatedDate, nil
+	return review, nil
 }
 
-func getDeployment(environment string, mergeDate time.Time, db dal.Dal) (*devops.CICDTask, errors.Error) {
+func getDeployment(mergeSha string, repoId string, deploymentPairList []deploymentPair, db dal.Dal) (*deploymentPair, errors.Error) {
 	// ignore environment at this point because detecting it by name is obviously not engouh
 	// take https://github.com/apache/incubator-devlake/actions/workflows/build.yml for example
 	// one can not distingush testing/production by looking at the job name solely.
-	cicdTask := &devops.CICDTask{}
-	cicdTaskClauses := []dal.Clause{
-		dal.From(&devops.CICDTask{}),
-		dal.Where(`
-			type = ?
-			AND cicd_tasks.result = ?
-			AND cicd_tasks.started_date > ?`,
-			"DEPLOYMENT",
-			"SUCCESS",
-			mergeDate,
-		),
-		dal.Orderby("cicd_tasks.started_date ASC"),
-		dal.Limit(1),
-	}
-	err := db.First(cicdTask, cicdTaskClauses...)
-	if goerror.Is(err, gorm.ErrRecordNotFound) {
-		return nil, nil
-	}
-	if err != nil {
-		return nil, err
+	commitDiff := &code.CommitsDiff{}
+	// find if tuple[merge_sha, new_commit_sha, old_commit_sha] exist in commits_diffs, if yes, return pair.FinishedDate
+	for _, pair := range deploymentPairList {
+		if repoId != pair.RepoId {
+			continue
+		}
+		err := db.First(commitDiff, dal.Where(`commit_sha = ? and new_commit_sha = ? and old_commit_sha = ?`,
+			mergeSha, pair.NewDeployCommitSha, pair.OldDeployCommitSha))
+		if err == nil {
+			return &pair, nil
+		}
+		if goerror.Is(err, gorm.ErrRecordNotFound) {
+			continue
+		}
+		if err != nil {
+			return nil, err
+		}
+
 	}
-	return cicdTask, nil
+	return nil, nil
 }
 
-func processNegativeValue(pr *code.PullRequest) {
-	if pr.OrigCodingTimespan > 0 {
-		pr.CodingTimespan = &pr.OrigCodingTimespan
+func processNegativeValue(v int64) *int64 {
+	if v > 0 {
+		return &v
 	} else {
-		pr.CodingTimespan = nil
-	}
-	if pr.OrigReviewLag > 0 {
-		pr.ReviewLag = &pr.OrigReviewLag
-	} else {
-		pr.ReviewLag = nil
-	}
-	if pr.OrigReviewTimespan > 0 {
-		pr.ReviewTimespan = &pr.OrigReviewTimespan
-	} else {
-		pr.ReviewTimespan = nil
-	}
-	if pr.OrigDeployTimespan > 0 {
-		pr.DeployTimespan = &pr.OrigDeployTimespan
-	} else {
-		pr.DeployTimespan = nil
+		return nil
 	}
 }
 
@@ -209,3 +232,11 @@ var CalculateChangeLeadTimeMeta = core.SubTaskMeta{
 	Description:      "Calculate change lead time",
 	DomainTypes:      []string{core.DOMAIN_TYPE_CICD, core.DOMAIN_TYPE_CODE},
 }
+
+type deploymentPair struct {
+	TaskId             string
+	RepoId             string
+	NewDeployCommitSha string
+	OldDeployCommitSha string
+	TaskFinishedDate   *time.Time
+}
diff --git a/plugins/dora/tasks/task_data.go b/plugins/dora/tasks/task_data.go
index 9345eaabf..16ce528af 100644
--- a/plugins/dora/tasks/task_data.go
+++ b/plugins/dora/tasks/task_data.go
@@ -23,6 +23,7 @@ import (
 )
 
 type DoraApiParams struct {
+	ProjectName string
 }
 
 type TransformationRules struct {
@@ -36,6 +37,7 @@ type DoraOptions struct {
 	Since               string
 	RepoId              string `json:"repoId"`
 	Prefix              string `json:"prefix"`
+	ProjectName         string `json:"projectName"`
 	TransformationRules `mapstructure:"transformationRules" json:"transformationRules"`
 }
 
diff --git a/plugins/jenkins/e2e/builds_test.go b/plugins/jenkins/e2e/builds_test.go
index 5a925c096..4fee31bef 100644
--- a/plugins/jenkins/e2e/builds_test.go
+++ b/plugins/jenkins/e2e/builds_test.go
@@ -41,15 +41,15 @@ func TestJenkinsBuildsDataFlow(t *testing.T) {
 		Job: &models.JenkinsJob{FullName: "Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake"},
 	}
 
-	// import raw data table
-	// SELECT * FROM _raw_jenkins_api_builds INTO OUTFILE "/tmp/_raw_jenkins_api_builds.csv" FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED BY '\r\n';
-	dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_jenkins_api_builds.csv", "_raw_jenkins_api_builds")
-
-	// verify extraction
 	dataflowTester.FlushTabler(&models.JenkinsBuild{})
 	dataflowTester.FlushTabler(&models.JenkinsBuildCommit{})
 	dataflowTester.FlushTabler(&models.JenkinsStage{})
 
+	// import raw data table
+	// SELECT * FROM _raw_jenkins_api_builds INTO OUTFILE "/tmp/_raw_jenkins_api_builds.csv" FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED BY '\r\n';
+	dataflowTester.ImportCsvIntoRawTable("./raw_tables/_raw_jenkins_api_builds.csv", "_raw_jenkins_api_builds")
+	dataflowTester.ImportCsvIntoTabler("./raw_tables/_tool_jenkins_stages.csv", &models.JenkinsStage{})
+
 	dataflowTester.Subtask(tasks.ExtractApiBuildsMeta, taskData)
 	dataflowTester.VerifyTable(
 		models.JenkinsBuild{},
@@ -65,6 +65,7 @@ func TestJenkinsBuildsDataFlow(t *testing.T) {
 			"result",
 			"timestamp",
 			"start_time",
+			"has_stages",
 		),
 	)
 
@@ -84,6 +85,18 @@ func TestJenkinsBuildsDataFlow(t *testing.T) {
 	dataflowTester.FlushTabler(&devops.CICDPipeline{})
 	dataflowTester.FlushTabler(&devops.CiCDPipelineCommit{})
 	dataflowTester.Subtask(tasks.EnrichApiBuildWithStagesMeta, taskData)
+	dataflowTester.VerifyTable(
+		models.JenkinsBuild{},
+		"./snapshot_tables/_tool_jenkins_builds_after_enrich.csv",
+		[]string{
+			"connection_id",
+			"job_name",
+			"duration",
+			"full_display_name",
+			"has_stages",
+		},
+	)
+
 	dataflowTester.Subtask(tasks.ConvertBuildsToCICDMeta, taskData)
 	dataflowTester.Subtask(tasks.ConvertBuildReposMeta, taskData)
 
diff --git a/plugins/jenkins/e2e/raw_tables/_raw_jenkins_api_jobs.csv b/plugins/jenkins/e2e/raw_tables/_raw_jenkins_api_jobs.csv
index 68a632b1b..b995fc323 100644
--- a/plugins/jenkins/e2e/raw_tables/_raw_jenkins_api_jobs.csv
+++ b/plugins/jenkins/e2e/raw_tables/_raw_jenkins_api_jobs.csv
@@ -1,2 +1,4 @@
 "id","params","data","url","input","created_at"
-16247,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_class"":""hudson.model.FreeStyleProject"",""actions"":[{},{},{},{},{""_class"":""com.cloudbees.plugins.credentials.ViewCredentialsAction""}],""primaryView"":{""_class"":""hudson.model.AllView"",""name"":""All"",""url"":""https://jenkins-zjk.merico.cn/job/Test-jenkins-dir/job/test-jenkins-sub-dir/""},""description"":""12121"",""displayName"":""test- [...]
\ No newline at end of file
+16247,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_class"":""hudson.model.FreeStyleProject"",""actions"":[{},{},{},{},{""_class"":""com.cloudbees.plugins.credentials.ViewCredentialsAction""}],""primaryView"":{""_class"":""hudson.model.AllView"",""name"":""All"",""url"":""https://jenkins-zjk.merico.cn/job/Test-jenkins-dir/job/test-jenkins-sub-dir/""},""description"":""12121"",""displayName"":""test- [...]
+16248,"{""ConnectionId"":2,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_class"":""hudson.model.FreeStyleProject"",""actions"":[{},{},{},{},{""_class"":""com.cloudbees.plugins.credentials.ViewCredentialsAction""}],""primaryView"":{""_class"":""hudson.model.AllView"",""name"":""All"",""url"":""https://jenkins-zjk.merico.cn/job/Test-jenkins-dir/job/test-jenkins-sub-dir/""},""description"":""12121"",""displayName"":""test- [...]
+16249,"{""ConnectionId"":1,""JobName"":""devlake1"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_class"":""hudson.model.FreeStyleProject"",""actions"":[{},{},{},{},{""_class"":""com.cloudbees.plugins.credentials.ViewCredentialsAction""}],""primaryView"":{""_class"":""hudson.model.AllView"",""name"":""All"",""url"":""https://jenkins-zjk.merico.cn/job/Test-jenkins-dir/job/test-jenkins-sub-dir/""},""description"":""12121"",""displayName"":""test [...]
diff --git a/plugins/jenkins/e2e/raw_tables/_raw_jenkins_api_stages.csv b/plugins/jenkins/e2e/raw_tables/_raw_jenkins_api_stages.csv
index ecae6ca92..eb3037a91 100644
--- a/plugins/jenkins/e2e/raw_tables/_raw_jenkins_api_stages.csv
+++ b/plugins/jenkins/e2e/raw_tables/_raw_jenkins_api_stages.csv
@@ -15,3 +15,6 @@
 13581,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_links"":{""self"":{""href"":""/job/Test%20Gitlab%20Sync/12/execution/node/6/wfapi/describe""}},""id"":""3"",""name"":""gitlabAutoSync"",""execNode"":"""",""status"":""SUCCESS"",""startTimeMillis"":1583981125465,""durationMillis"":83641,""pauseDurationMillis"":0}",https://jenkins.merico.cn/job/Test%20Gitlab%20Sync/12/wfapi/describe,"{""Number"": ""1 [...]
 13582,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_links"":{""self"":{""href"":""/job/Auto%20Init%20Gitlab/23/execution/node/6/wfapi/describe""}},""id"":""4"",""name"":""gitlabInit"",""execNode"":"""",""status"":null,""error"":{""message"":null,""type"":""org.jenkinsci.plugins.workflow.steps.FlowInterruptedException""},""startTimeMillis"":1605767269680,""durationMillis"":248279,""pauseDurationMilli [...]
 13583,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_links"":{""self"":{""href"":""/job/Test%20Gitlab%20Sync/10/execution/node/6/wfapi/describe""}},""id"":""5"",""name"":""gitlabAutoSync"",""execNode"":"""",""status"":""SUCCESS"",""startTimeMillis"":1583981120136,""durationMillis"":86044,""pauseDurationMillis"":0}",https://jenkins.merico.cn/job/Test%20Gitlab%20Sync/10/wfapi/describe,"{""Number"": ""1 [...]
+13584,"{""ConnectionId"":2,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_links"":{""self"":{""href"":""/job/Test%20Gitlab%20Sync/12/execution/node/6/wfapi/describe""}},""id"":""3"",""name"":""gitlabAutoSync"",""execNode"":"""",""status"":""SUCCESS"",""startTimeMillis"":1583981125465,""durationMillis"":83641,""pauseDurationMillis"":0}",https://jenkins.merico.cn/job/Test%20Gitlab%20Sync/12/wfapi/describe,"{""Number"": ""1 [...]
+13585,"{""ConnectionId"":2,""JobName"":""devlake1"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_links"":{""self"":{""href"":""/job/Auto%20Init%20Gitlab/23/execution/node/6/wfapi/describe""}},""id"":""4"",""name"":""gitlabInit"",""execNode"":"""",""status"":null,""error"":{""message"":null,""type"":""org.jenkinsci.plugins.workflow.steps.FlowInterruptedException""},""startTimeMillis"":1605767269680,""durationMillis"":248279,""pauseDurationMill [...]
+13586,"{""ConnectionId"":3,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}","{""_links"":{""self"":{""href"":""/job/Test%20Gitlab%20Sync/10/execution/node/6/wfapi/describe""}},""id"":""5"",""name"":""gitlabAutoSync"",""execNode"":"""",""status"":""SUCCESS"",""startTimeMillis"":1583981120136,""durationMillis"":86044,""pauseDurationMillis"":0}",https://jenkins.merico.cn/job/Test%20Gitlab%20Sync/10/wfapi/describe,"{""Number"": ""1 [...]
diff --git a/plugins/jenkins/e2e/raw_tables/_tool_jenkins_builds_for_stages.csv b/plugins/jenkins/e2e/raw_tables/_tool_jenkins_builds_for_stages.csv
index 3fbc8469c..8ca916477 100644
--- a/plugins/jenkins/e2e/raw_tables/_tool_jenkins_builds_for_stages.csv
+++ b/plugins/jenkins/e2e/raw_tables/_tool_jenkins_builds_for_stages.csv
@@ -13,3 +13,5 @@ connection_id,full_display_name,job_name,job_path,duration,estimated_duration,nu
 1,Test Gitlab Sync #12,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,4,9,3,SUCCESS,1658386255052,2022-07-21T06:50:55.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,110,
 1,Auto Init Gitlab #23,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,11,9,4,SUCCESS,1662647217746,2022-09-08T14:26:57.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,109,
 1,Test Gitlab Sync #10,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1429,745,1,SUCCESS,1658385602419,2022-07-21T06:40:02.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,97,
+2,Auto Init Gitlab #23,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,11,9,4,SUCCESS,1662647217746,2022-09-08T14:26:57.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,109,
+2,Test Gitlab Sync #10,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1429,745,1,SUCCESS,1658385602419,2022-07-21T06:40:02.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,97,
diff --git a/plugins/jenkins/e2e/raw_tables/_tool_jenkins_stages.csv b/plugins/jenkins/e2e/raw_tables/_tool_jenkins_stages.csv
new file mode 100644
index 000000000..e7fbd0784
--- /dev/null
+++ b/plugins/jenkins/e2e/raw_tables/_tool_jenkins_stages.csv
@@ -0,0 +1,20 @@
+connection_id,id,build_name,name,exec_node,status,start_time_millis,duration_millis,pause_duration_millis,type,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #11,gitlabAutoSync,,SUCCESS,1581076468986,14118,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13579,
+1,10,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #13,Hello,,SUCCESS,1662651617917,258,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,5,
+1,11,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #15,Hello,,SUCCESS,1662651634424,79,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,6,
+1,12,Auto Init Gitlab #1,gitlabInit,,FAILED,1583329644046,588,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13574,
+1,2,Auto Init Gitlab #19,gitlabInit,,ABORTED,1584497957140,5859,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13580,
+1,3,Test Gitlab Sync #12,gitlabAutoSync,,SUCCESS,1583981125465,83641,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13581,
+1,4,Auto Init Gitlab #23,gitlabInit,,,1605767269680,248279,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13582,
+1,5,Test Gitlab Sync #10,gitlabAutoSync,,SUCCESS,1583981120136,86044,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13583,
+1,6,Auto Init Gitlab #18,gitlabInit,,FAILED,1584458835920,215100,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13575,
+1,6,pipeline-test2 #2,Hello,,SUCCESS,1662651634424,79,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,1,
+1,7,Auto Init Gitlab #19,gitlabInit,,ABORTED,1584497957140,5859,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13576,
+1,7,pipeline-test2 #3,Hello,,SUCCESS,1662651649629,122,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,2,
+1,8,Pipeline expirement #5,scp-f/b,,FAILED,1572321694770,297,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13577,
+1,8,pipeline-test2 #3,Hello,,SUCCESS,1662651649629,122,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,3,
+1,9,Auto Init Gitlab #58,gitlabInit,,,1615296910614,1312274,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13578,
+1,9,pipeline-test2 #1,Hello,,SUCCESS,1662651617917,258,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,4,
+2,8,pipeline-test2 #3,Hello,,SUCCESS,1662651649629,122,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,3,
+2,9,Auto Init Gitlab #58,gitlabInit,,,1615296910614,1312274,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,13578,
+2,9,pipeline-test2 #1,Hello,,SUCCESS,1662651617917,258,0,,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_stages,4,
diff --git a/plugins/jenkins/e2e/snapshot_tables/_tool_jenkins_builds.csv b/plugins/jenkins/e2e/snapshot_tables/_tool_jenkins_builds.csv
index e33e71886..62e819449 100644
--- a/plugins/jenkins/e2e/snapshot_tables/_tool_jenkins_builds.csv
+++ b/plugins/jenkins/e2e/snapshot_tables/_tool_jenkins_builds.csv
@@ -1,30 +1,30 @@
-connection_id,full_display_name,job_name,job_path,duration,estimated_duration,number,result,timestamp,start_time,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
-1,pipeline-test2 #1,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,4564,1972,1,SUCCESS,1662651613681,2022-09-08T15:40:13.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,100,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #11,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,14820,1457,1,SUCCESS,1650017416514,2022-04-15T10:10:16.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,95,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #13,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1429,745,1,SUCCESS,1658385602419,2022-07-21T06:40:02.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,97,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #15,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,70,27,1,SUCCESS,1658385566471,2022-07-21T06:39:26.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,105,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #17,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,57,6,1,SUCCESS,1650017153775,2022-04-15T10:05:53.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,124,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #170,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,12,6,10,SUCCESS,1662647233074,2022-09-08T14:27:13.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,115,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #171,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,4,6,11,SUCCESS,1662651656567,2022-09-08T15:40:56.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,114,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #172,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,2,6,12,SUCCESS,1662651657893,2022-09-08T15:40:57.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,113,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #21,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,2121,1457,2,SUCCESS,1650022548450,2022-04-15T11:35:48.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,94,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #215,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,11,8,2,SUCCESS,1662647212436,2022-09-08T14:26:52.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,101,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #23,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,61,745,2,SUCCESS,1662647211512,2022-09-08T14:26:51.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,96,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #24,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,551,1972,2,SUCCESS,1662651633991,2022-09-08T15:40:33.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,99,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #25,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,6,27,2,SUCCESS,1658385576367,2022-07-21T06:39:36.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,104,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #27,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,4,6,2,SUCCESS,1650017177939,2022-04-15T10:06:17.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,123,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #31,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1587,1457,3,SUCCESS,1650024049161,2022-04-15T12:00:49.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,93,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #34,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,802,1972,3,SUCCESS,1662651648992,2022-09-08T15:40:48.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,98,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #35,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,4,27,3,SUCCESS,1662647217041,2022-09-08T14:26:57.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,103,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #37,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,3,6,3,SUCCESS,1650017186253,2022-04-15T10:06:26.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,122,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #41,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,13952,1457,4,SUCCESS,1662647203905,2022-09-08T14:26:43.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,92,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #47,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,6,6,4,SUCCESS,1650022556910,2022-04-15T11:35:56.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,121,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #51,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1274,1457,5,SUCCESS,1662647231332,2022-09-08T14:27:11.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,91,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #57,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,6,6,5,SUCCESS,1650022558491,2022-04-15T11:35:58.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,120,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #61,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1202,1457,6,SUCCESS,1662647242809,2022-09-08T14:27:22.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,90,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #67,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,10,6,6,SUCCESS,1650022560954,2022-04-15T11:36:00.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,119,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #71,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1988,1457,7,SUCCESS,1662651625889,2022-09-08T15:40:25.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,89,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #77,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,8,6,7,SUCCESS,1650023883294,2022-04-15T11:58:03.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,118,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #81,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1180,1457,8,SUCCESS,1662651640536,2022-09-08T15:40:40.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,88,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #87,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,11,6,8,SUCCESS,1650023894336,2022-04-15T11:58:14.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,117,
-1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #97,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,78,6,9,SUCCESS,1662647207972,2022-09-08T14:26:47.000+00:00,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,116,
+connection_id,full_display_name,job_name,job_path,duration,estimated_duration,number,result,timestamp,start_time,has_stages,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
+1,pipeline-test2 #1,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,4564,1972,1,SUCCESS,1662651613681,2022-09-08T15:40:13.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,100,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #11,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,14820,1457,1,SUCCESS,1650017416514,2022-04-15T10:10:16.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,95,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #13,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1429,745,1,SUCCESS,1658385602419,2022-07-21T06:40:02.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,97,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #15,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,70,27,1,SUCCESS,1658385566471,2022-07-21T06:39:26.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,105,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #17,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,57,6,1,SUCCESS,1650017153775,2022-04-15T10:05:53.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,124,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #170,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,12,6,10,SUCCESS,1662647233074,2022-09-08T14:27:13.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,115,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #171,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,4,6,11,SUCCESS,1662651656567,2022-09-08T15:40:56.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,114,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #172,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,2,6,12,SUCCESS,1662651657893,2022-09-08T15:40:57.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,113,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #21,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,2121,1457,2,SUCCESS,1650022548450,2022-04-15T11:35:48.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,94,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #215,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,11,8,2,SUCCESS,1662647212436,2022-09-08T14:26:52.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,101,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #23,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,61,745,2,SUCCESS,1662647211512,2022-09-08T14:26:51.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,96,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #24,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,551,1972,2,SUCCESS,1662651633991,2022-09-08T15:40:33.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,99,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #25,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,6,27,2,SUCCESS,1658385576367,2022-07-21T06:39:36.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,104,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #27,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,4,6,2,SUCCESS,1650017177939,2022-04-15T10:06:17.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,123,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #31,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1587,1457,3,SUCCESS,1650024049161,2022-04-15T12:00:49.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,93,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #34,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,802,1972,3,SUCCESS,1662651648992,2022-09-08T15:40:48.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,98,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #35,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,4,27,3,SUCCESS,1662647217041,2022-09-08T14:26:57.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,103,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #37,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,3,6,3,SUCCESS,1650017186253,2022-04-15T10:06:26.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,122,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #41,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,13952,1457,4,SUCCESS,1662647203905,2022-09-08T14:26:43.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,92,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #47,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,6,6,4,SUCCESS,1650022556910,2022-04-15T11:35:56.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,121,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #51,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1274,1457,5,SUCCESS,1662647231332,2022-09-08T14:27:11.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,91,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #57,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,6,6,5,SUCCESS,1650022558491,2022-04-15T11:35:58.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,120,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #61,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1202,1457,6,SUCCESS,1662647242809,2022-09-08T14:27:22.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,90,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #67,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,10,6,6,SUCCESS,1650022560954,2022-04-15T11:36:00.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,119,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #71,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1988,1457,7,SUCCESS,1662651625889,2022-09-08T15:40:25.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,89,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #77,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,8,6,7,SUCCESS,1650023883294,2022-04-15T11:58:03.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,118,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #81,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,1180,1457,8,SUCCESS,1662651640536,2022-09-08T15:40:40.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,88,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #87,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,11,6,8,SUCCESS,1650023894336,2022-04-15T11:58:14.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,117,
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #97,devlake,job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/,78,6,9,SUCCESS,1662647207972,2022-09-08T14:26:47.000+00:00,0,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,116,
diff --git a/plugins/jenkins/e2e/snapshot_tables/_tool_jenkins_builds_after_enrich.csv b/plugins/jenkins/e2e/snapshot_tables/_tool_jenkins_builds_after_enrich.csv
new file mode 100644
index 000000000..fa929f45e
--- /dev/null
+++ b/plugins/jenkins/e2e/snapshot_tables/_tool_jenkins_builds_after_enrich.csv
@@ -0,0 +1,30 @@
+connection_id,full_display_name,job_name,duration,has_stages
+1,pipeline-test2 #1,devlake,4564,1
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #11,devlake,14820,1
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #13,devlake,1429,1
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #15,devlake,70,1
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #17,devlake,57,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #170,devlake,12,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #171,devlake,4,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #172,devlake,2,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #21,devlake,2121,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #215,devlake,11,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #23,devlake,61,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #24,devlake,551,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #25,devlake,6,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #27,devlake,4,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #31,devlake,1587,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #34,devlake,802,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #35,devlake,4,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #37,devlake,3,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #41,devlake,13952,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #47,devlake,6,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #51,devlake,1274,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #57,devlake,6,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #61,devlake,1202,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #67,devlake,10,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #71,devlake,1988,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #77,devlake,8,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #81,devlake,1180,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #87,devlake,11,0
+1,Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #97,devlake,78,0
diff --git a/plugins/jenkins/e2e/snapshot_tables/cicd_tasks.csv b/plugins/jenkins/e2e/snapshot_tables/cicd_tasks.csv
index cb798e9a3..ef155677d 100644
--- a/plugins/jenkins/e2e/snapshot_tables/cicd_tasks.csv
+++ b/plugins/jenkins/e2e/snapshot_tables/cicd_tasks.csv
@@ -1,8 +1,4 @@
 id,name,pipeline_id,result,status,type,environment,duration_sec,started_date,finished_date,cicd_scope_id,_raw_data_params,_raw_data_table,_raw_data_id,_raw_data_remark
-jenkins:JenkinsBuild:1:pipeline-test2 #1,devlake,jenkins:JenkinsBuild:1:pipeline-test2 #1,SUCCESS,DONE,,,4,2022-09-08T15:40:13.000+00:00,2022-09-08T15:40:17.000+00:00,jenkins:JenkinsJob:1:Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir/""}",_raw_jenkins_api_builds,100,
-jenkins:JenkinsBuild:1:Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #11,devlake,jenkins:JenkinsBuild:1:Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #11,SUCCESS,DONE,,,14,2022-04-15T10:10:16.000+00:00,2022-04-15T10:10:30.000+00:00,jenkins:JenkinsJob:1:Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-di [...]
-jenkins:JenkinsBuild:1:Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #13,devlake,jenkins:JenkinsBuild:1:Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #13,SUCCESS,DONE,,,1,2022-07-21T06:40:02.000+00:00,2022-07-21T06:40:03.000+00:00,jenkins:JenkinsJob:1:Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir [...]
-jenkins:JenkinsBuild:1:Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #15,devlake,jenkins:JenkinsBuild:1:Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #15,SUCCESS,DONE,,,0,2022-07-21T06:39:26.000+00:00,2022-07-21T06:39:26.000+00:00,jenkins:JenkinsJob:1:Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir [...]
 jenkins:JenkinsBuild:1:Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #17,devlake,jenkins:JenkinsBuild:1:Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #17,SUCCESS,DONE,,,0,2022-04-15T10:05:53.000+00:00,2022-04-15T10:05:53.000+00:00,jenkins:JenkinsJob:1:Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-dir [...]
 jenkins:JenkinsBuild:1:Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #170,devlake,jenkins:JenkinsBuild:1:Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #170,SUCCESS,DONE,,,0,2022-09-08T14:27:13.000+00:00,2022-09-08T14:27:13.000+00:00,jenkins:JenkinsJob:1:Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-d [...]
 jenkins:JenkinsBuild:1:Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #171,devlake,jenkins:JenkinsBuild:1:Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake #171,SUCCESS,DONE,,,0,2022-09-08T15:40:56.000+00:00,2022-09-08T15:40:56.000+00:00,jenkins:JenkinsJob:1:Test-jenkins-dir » test-jenkins-sub-dir » test-sub-sub-dir » devlake,"{""ConnectionId"":1,""JobName"":""devlake"",""JobPath"":""job/Test-jenkins-dir/job/test-jenkins-sub-dir/job/test-sub-sub-d [...]
diff --git a/plugins/jenkins/tasks/build_stages_enricher.go b/plugins/jenkins/tasks/build_stages_enricher.go
index d223e66e3..8ce74eb21 100644
--- a/plugins/jenkins/tasks/build_stages_enricher.go
+++ b/plugins/jenkins/tasks/build_stages_enricher.go
@@ -22,12 +22,8 @@ import (
 	"github.com/apache/incubator-devlake/plugins/core"
 	"github.com/apache/incubator-devlake/plugins/core/dal"
 	"github.com/apache/incubator-devlake/plugins/jenkins/models"
-	"strconv"
-	"strings"
 )
 
-// this struct should be moved to `gitub_api_common.go`
-
 var EnrichApiBuildWithStagesMeta = core.SubTaskMeta{
 	Name:             "enrichApiBuildWithStages",
 	EntryPoint:       EnrichApiBuildWithStages,
@@ -40,15 +36,14 @@ func EnrichApiBuildWithStages(taskCtx core.SubTaskContext) errors.Error {
 	data := taskCtx.GetData().(*JenkinsTaskData)
 	db := taskCtx.GetDal()
 	clauses := []dal.Clause{
-		dal.Select("distinct build_name"),
-		dal.From(&models.JenkinsStage{}),
-		dal.Join(`left join _tool_jenkins_builds tjb 
-						on _tool_jenkins_stages.build_name = tjb.full_display_name 
-						and _tool_jenkins_stages.connection_id = tjb.connection_id`),
-		dal.Where(`_tool_jenkins_stages.connection_id = ? 
+		dal.Select("tjb.*"),
+		dal.From(`_tool_jenkins_builds tjb`),
+		dal.Join(`inner join _tool_jenkins_stages tjs 
+						on tjs.build_name = tjb.full_display_name 
+						and tjs.connection_id = tjb.connection_id`),
+		dal.Where(`tjb.connection_id = ? 
 							and tjb.job_path = ? and tjb.job_name = ?`,
 			data.Options.ConnectionId, data.Options.JobPath, data.Options.JobName),
-		dal.Groupby("_tool_jenkins_stages.build_name"),
 	}
 	cursor, err := db.Cursor(clauses...)
 	if err != nil {
@@ -58,35 +53,17 @@ func EnrichApiBuildWithStages(taskCtx core.SubTaskContext) errors.Error {
 	taskCtx.SetProgress(0, -1)
 
 	for cursor.Next() {
-		var buildName string
-		err = errors.Convert(cursor.Scan(&buildName))
-		if err != nil {
-			return err
-		}
-		if buildName == "" {
-			continue
-		}
 		build := &models.JenkinsBuild{}
-		build.ConnectionId = data.Options.ConnectionId
-		str := strings.Split(buildName, "#")
-		build.JobName = strings.TrimSpace(str[0])
-		var number int
-		number, err = errors.Convert01(strconv.Atoi(strings.TrimSpace(str[1])))
+		err = db.Fetch(cursor, build)
 		if err != nil {
 			return err
 		}
-		build.Number = int64(number)
-		err = db.First(build)
-		if err != nil {
-			return errors.Convert(err)
-		}
 		build.HasStages = true
-
-		err = db.Update(build)
+		err = db.CreateOrUpdate(build)
 		if err != nil {
-			return errors.Convert(err)
+			return err
 		}
-		taskCtx.IncProgress(1)
 	}
+
 	return nil
 }
diff --git a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions.csv b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions.csv
index 019b75cdb..6f48b125c 100644
--- a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions.csv
+++ b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_executions.csv
@@ -1,2 +1,2 @@
 id,params,data,url,input,created_at
-1,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":1,""project"":7,""model"":"""",""type"":""sprint"",""lifetime"":""sprint"",""budget"":""0"",""budgetUnit"":""CNY"",""attribute"":"""",""percent"":0,""milestone"":""0"",""output"":"""",""auth"":"""",""parent"":7,""path"":"",7,1,"",""grade"":1,""name"":""\u4f01\u4e1a\u7f51\u7ad9\u7b2c\u4e00\u671f"",""code"":""coWeb1"",""begin"":""2020-06-05"",""end"":""2021-12-04"",""realBegan"":null,""realEnd"":null,""days [...]
+1,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":1,""project"":7,""model"":"""",""type"":""sprint"",""lifetime"":""sprint"",""budget"":""0"",""budgetUnit"":""CNY"",""attribute"":"""",""percent"":0,""milestone"":""0"",""output"":"""",""auth"":"""",""parent"":7,""path"":"",7,1,"",""grade"":1,""name"":""\u4f01\u4e1a\u7f51\u7ad9\u7b2c\u4e00\u671f"",""code"":""coWeb1"",""begin"":""2020-06-05"",""end"":""2021-12-04"",""realBegan"":null,""realEnd"":null,""days [...]
diff --git a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_products.csv b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_products.csv
index e58feada0..ccfdbc5d5 100644
--- a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_products.csv
+++ b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_products.csv
@@ -1,2 +1,2 @@
 id,params,data,url,input,created_at
-4,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":9,""ProjectId"":1}","{""id"":3,""program"":10,""name"":""\u4ea7\u54c1\u540d\u79f01"",""code"":""\u4ea7\u54c1\u4ee3\u53f72"",""bind"":""0"",""line"":31,""type"":""normal"",""status"":""normal"",""subStatus"":"""",""desc"":""\u003Cspan style=\""background-color:#FFFFFF;\""\u003E\u4ea7\u54c1\u63cf\u8ff01\u003C\/span\u003E"",""PO"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""QD"":{""id"":1,""account"":"" [...]
+4,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":9,""ProjectId"":1}","{""id"":3,""program"":10,""name"":""\u4ea7\u54c1\u540d\u79f01"",""code"":""\u4ea7\u54c1\u4ee3\u53f72"",""bind"":""0"",""line"":31,""type"":""normal"",""status"":""normal"",""subStatus"":"""",""desc"":""\u003Cspan style=\""background-color:#FFFFFF;\""\u003E\u4ea7\u54c1\u63cf\u8ff01\u003C\/span\u003E"",""PO"":{""id"":1,""account"":""devlake"",""avatar"":"""",""realname"":""devlake""},""QD"":{""id"":1,""account"":"" [...]
diff --git a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_tasks.csv b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_tasks.csv
index 8aa6a4489..73a83d8f6 100644
--- a/plugins/zentao/e2e/raw_tables/_raw_zentao_api_tasks.csv
+++ b/plugins/zentao/e2e/raw_tables/_raw_zentao_api_tasks.csv
@@ -1,4 +1,4 @@
 id,params,data,url,input,created_at
-1,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":1,""project"":13,""parent"":0,""execution"":9,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":0,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":"" [...]
-2,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":2,""project"":13,""parent"":0,""execution"":9,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":0,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":"" [...]
-4,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":3,""project"":13,""parent"":0,""execution"":9,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":0,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":"" [...]
+1,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":1,""project"":13,""parent"":0,""execution"":9,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":0,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":"" [...]
+2,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":2,""project"":13,""parent"":0,""execution"":9,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":0,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":"" [...]
+4,"{""ConnectionId"":1,""ProductId"":3,""ExecutionId"":1,""ProjectId"":1}","{""id"":3,""project"":13,""parent"":0,""execution"":9,""module"":0,""design"":0,""story"":0,""storyVersion"":1,""designVersion"":0,""fromBug"":0,""feedback"":0,""fromIssue"":0,""name"":""\u4efb\u52a1\u540d\u79f0"",""type"":""devel"",""mode"":"""",""pri"":3,""estimate"":0,""consumed"":0,""left"":0,""deadline"":""2022-10-01"",""status"":""wait"",""subStatus"":"""",""color"":"""",""mailto"":[{""id"":2,""account"":"" [...]
diff --git a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bugs.csv b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bugs.csv
index da82aa928..1f49d1a4c 100644
--- a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bugs.csv
+++ b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_bugs.csv
@@ -1,4 +1,4 @@
-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
diff --git a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_executions.csv b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_executions.csv
index 05762df6f..74ccea041 100644
--- a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_executions.csv
+++ b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_executions.csv
@@ -1,2 +1,2 @@
 connection_id,id,project,model,type,lifetime,budget,budget_unit,attribute,percent,milestone,output,auth,parent,path,grade,name,code,plan_begin,plan_end,real_began,real_end,days,status,sub_status,pri,description,version,parent_version,plan_duration,real_duration,opened_by_id,opened_date,opened_version,last_edited_by_id,last_edited_date,closed_by_id,closed_date,canceled_by_id,canceled_date,suspended_date,po_id,pm_id,qd_id,rd_id,team,acl,order_in,vision,display_cards,fluid_board,deleted,tot [...]
-1,1,7,,sprint,sprint,0,CNY,,0,0,,,7,",7,1,",1,企业网站第一期,coWeb1,2020-06-05,2021-12-04,,,391,doing,,1,开发企业网站的基本雏形。<br />,0,0,0,0,0,,,0,,0,,0,,0000-00-00,2,3,10,2,公司开发团队,open,5,rnd,0,0,0,11753,52,40,29,0,58,0
+1,1,7,,sprint,sprint,0,CNY,,0,0,,,7,",7,1,",1,企业网站第一期,coWeb1,2020-06-05,2021-12-04,,,391,doing,,1,开发企业网站的基本雏形。<br />,0,0,0,0,0,,,0,,0,,0,,0000-00-00,2,3,10,2,公司开发团队,open,5,rnd,0,0,0,11753,52,40,29,0,58.921,0
diff --git a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_products.csv b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_products.csv
index b0bd40c9c..eff1a9f33 100644
--- a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_products.csv
+++ b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_products.csv
@@ -1,2 +1,2 @@
 connection_id,id,program,name,code,bind,line,type,status,sub_status,description,po_id,qd_id,rd_id,acl,reviewer,created_by_id,created_date,created_version,order_in,deleted,plans,releases,builds,cases,projects,executions,bugs,docs,progress,case_review
-1,3,10,产品名称1,产品代号2,0,31,normal,normal,,"<span style=""background-color:#FFFFFF;"">产品描述1</span>",1,1,1,private,"devlake,dev1",1,2022-11-17T06:42:25.000+00:00,17.6,15,0,1,0,0,0,0,0,0,0,0,0
+1,3,10,产品名称1,产品代号2,0,31,normal,normal,,"<span style=""background-color:#FFFFFF;"">产品描述1</span>",1,1,1,private,"devlake,dev1",1,2022-11-17T06:42:25.000+00:00,17.6,15,0,1,0,0,0,0,0,0,0,12.121,0
diff --git a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_tasks.csv b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_tasks.csv
index 276d53cda..4125a08dc 100644
--- a/plugins/zentao/e2e/snapshot_tables/_tool_zentao_tasks.csv
+++ b/plugins/zentao/e2e/snapshot_tables/_tool_zentao_tasks.csv
@@ -1,4 +1,4 @@
-connection_id,id,execution_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_edite [...]
-1,1,1,13,0,9,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,,0000-00-00 00:00:00,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,0
-1,2,1,13,0,9,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,,0000-00-00 00:00:00,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,0
-1,3,1,13,0,9,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,,0000-00-00 00:00:00,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,0
+connection_id,id,execution_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_edite [...]
+1,1,1,13,0,9,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,,0000-00-00 00:00:00,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,21.11
+1,2,1,13,0,9,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,,0000-00-00 00:00:00,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,3
+1,3,1,13,0,9,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,,0000-00-00 00:00:00,0,0,0,,,,,0,rnd,0,,0,0,,开发乙,3,0,43.22121
diff --git a/plugins/zentao/models/archived/bug.go b/plugins/zentao/models/archived/bug.go
index a316cd809..84dd06945 100644
--- a/plugins/zentao/models/archived/bug.go
+++ b/plugins/zentao/models/archived/bug.go
@@ -78,7 +78,7 @@ type ZentaoBug struct {
 	Repo           int        `json:"repo"`
 	Mr             int        `json:"mr"`
 	Entry          string     `json:"entry"`
-	Lines          string     `json:"lines"`
+	NumOfLine      string     `json:"lines"`
 	V1             string     `json:"v1"`
 	V2             string     `json:"v2"`
 	RepoType       string     `json:"repoType"`
diff --git a/plugins/zentao/models/archived/execution.go b/plugins/zentao/models/archived/execution.go
index cca92bd95..5124b6473 100644
--- a/plugins/zentao/models/archived/execution.go
+++ b/plugins/zentao/models/archived/execution.go
@@ -80,8 +80,8 @@ type ZentaoExecution struct {
 	TotalConsumed  int    `json:"totalConsumed"`
 	TotalLeft      int    `json:"totalLeft"`
 	ProjectId      uint64
-	Progress       int  `json:"progress"`
-	CaseReview     bool `json:"caseReview"`
+	Progress       float64 `json:"progress"`
+	CaseReview     bool    `json:"caseReview"`
 	archived.NoPKModel
 }
 
diff --git a/plugins/zentao/models/archived/product.go b/plugins/zentao/models/archived/product.go
index c6977a099..f99872a7a 100644
--- a/plugins/zentao/models/archived/product.go
+++ b/plugins/zentao/models/archived/product.go
@@ -52,7 +52,7 @@ type ZentaoProduct struct {
 	Executions     int        `json:"executions"`
 	Bugs           int        `json:"bugs"`
 	Docs           int        `json:"docs"`
-	Progress       int        `json:"progress"`
+	Progress       float64    `json:"progress"`
 	CaseReview     bool       `json:"caseReview"`
 	archived.NoPKModel
 }
diff --git a/plugins/zentao/models/archived/project.go b/plugins/zentao/models/archived/project.go
index 8d884420b..b6f93d6b4 100644
--- a/plugins/zentao/models/archived/project.go
+++ b/plugins/zentao/models/archived/project.go
@@ -82,11 +82,11 @@ type ZentaoProject struct {
 	TeamCount      int    `json:"teamCount"`
 	LeftTasks      string `json:"leftTasks"`
 	//TeamMembers   []interface{} `json:"teamMembers" gorm:"-"`
-	TotalEstimate int `json:"totalEstimate"`
-	TotalConsumed int `json:"totalConsumed"`
-	TotalLeft     int `json:"totalLeft"`
-	Progress      int `json:"progress"`
-	TotalReal     int `json:"totalReal"`
+	TotalEstimate int     `json:"totalEstimate"`
+	TotalConsumed int     `json:"totalConsumed"`
+	TotalLeft     int     `json:"totalLeft"`
+	Progress      float64 `json:"progress"`
+	TotalReal     int     `json:"totalReal"`
 }
 type PM struct {
 	PmId       uint64 `json:"id"`
@@ -101,11 +101,11 @@ type Whitelist []struct {
 	WhitelistRealname string `json:"realname"`
 }
 type Hours struct {
-	HoursTotalEstimate int `json:"totalEstimate"`
-	HoursTotalConsumed int `json:"totalConsumed"`
-	HoursTotalLeft     int `json:"totalLeft"`
-	HoursProgress      int `json:"progress"`
-	HoursTotalReal     int `json:"totalReal"`
+	HoursTotalEstimate int     `json:"totalEstimate"`
+	HoursTotalConsumed int     `json:"totalConsumed"`
+	HoursTotalLeft     int     `json:"totalLeft"`
+	HoursProgress      float64 `json:"progress"`
+	HoursTotalReal     int     `json:"totalReal"`
 }
 
 func (ZentaoProject) TableName() string {
diff --git a/plugins/zentao/models/archived/task.go b/plugins/zentao/models/archived/task.go
index 4fd3533d3..0ee2d0568 100644
--- a/plugins/zentao/models/archived/task.go
+++ b/plugins/zentao/models/archived/task.go
@@ -76,7 +76,7 @@ type ZentaoTask struct {
 	Repo               int        `json:"repo"`
 	Mr                 int        `json:"mr"`
 	Entry              string     `json:"entry"`
-	Lines              string     `json:"lines"`
+	NumOfLine          string     `json:"lines"`
 	V1                 string     `json:"v1"`
 	V2                 string     `json:"v2"`
 	Deleted            bool       `json:"deleted"`
@@ -89,7 +89,7 @@ type ZentaoTask struct {
 	AssignedToRealName string     `json:"assignedToRealName"`
 	PriOrder           string     `json:"priOrder"`
 	NeedConfirm        bool       `json:"needConfirm"`
-	Progress           int        `json:"progress"`
+	Progress           float64    `json:"progress"`
 }
 
 func (ZentaoTask) TableName() string {
diff --git a/plugins/zentao/models/bug.go b/plugins/zentao/models/bug.go
index ed0d65599..a04fedac6 100644
--- a/plugins/zentao/models/bug.go
+++ b/plugins/zentao/models/bug.go
@@ -94,7 +94,7 @@ type ZentaoBugRes struct {
 	Repo         int        `json:"repo"`
 	Mr           int        `json:"mr"`
 	Entry        string     `json:"entry"`
-	Lines        string     `json:"lines"`
+	NumOfLine    string     `json:"lines"`
 	V1           string     `json:"v1"`
 	V2           string     `json:"v2"`
 	RepoType     string     `json:"repoType"`
@@ -171,7 +171,7 @@ type ZentaoBug struct {
 	Repo           int        `json:"repo"`
 	Mr             int        `json:"mr"`
 	Entry          string     `json:"entry"`
-	Lines          string     `json:"lines"`
+	NumOfLine      string     `json:"lines"`
 	V1             string     `json:"v1"`
 	V2             string     `json:"v2"`
 	RepoType       string     `json:"repoType"`
diff --git a/plugins/zentao/models/execution.go b/plugins/zentao/models/execution.go
index 38768ae96..f58d97df4 100644
--- a/plugins/zentao/models/execution.go
+++ b/plugins/zentao/models/execution.go
@@ -178,7 +178,7 @@ type ZentaoExecutionRes struct {
 		FluidBoard     string `json:"fluidBoard"`
 		Deleted        string `json:"deleted"`
 	} `json:"projectInfo"`
-	Progress    int `json:"progress"`
+	Progress    float64 `json:"progress"`
 	TeamMembers []struct {
 		ID         uint64 `json:"id"`
 		Root       int    `json:"root"`
@@ -207,8 +207,8 @@ type ZentaoExecutionRes struct {
 }
 
 type ZentaoExecution struct {
-	ConnectionId   uint64     `gorm:"primaryKey"`
-	Id             uint64     `json:"id" gorm:"primaryKey"`
+	ConnectionId   uint64     `gorm:"primaryKey;type:BIGINT  NOT NULL"`
+	Id             uint64     `json:"id" gorm:"primaryKey;type:BIGINT  NOT NULL"`
 	Project        uint64     `json:"project"`
 	Model          string     `json:"model"`
 	Type           string     `json:"type"`
@@ -264,8 +264,8 @@ type ZentaoExecution struct {
 	TotalConsumed  int    `json:"totalConsumed"`
 	TotalLeft      int    `json:"totalLeft"`
 	ProjectId      uint64
-	Progress       int  `json:"progress"`
-	CaseReview     bool `json:"caseReview"`
+	Progress       float64 `json:"progress"`
+	CaseReview     bool    `json:"caseReview"`
 	common.NoPKModel
 }
 
diff --git a/plugins/zentao/models/product.go b/plugins/zentao/models/product.go
index 802e8668d..ef5cd8418 100644
--- a/plugins/zentao/models/product.go
+++ b/plugins/zentao/models/product.go
@@ -74,21 +74,21 @@ type ZentaoProductRes struct {
 		Closed    int `json:"closed"`
 		Changing  int `json:"changing"`
 	} `json:"stories"`
-	Plans      int  `json:"plans"`
-	Releases   int  `json:"releases"`
-	Builds     int  `json:"builds"`
-	Cases      int  `json:"cases"`
-	Projects   int  `json:"projects"`
-	Executions int  `json:"executions"`
-	Bugs       int  `json:"bugs"`
-	Docs       int  `json:"docs"`
-	Progress   int  `json:"progress"`
-	CaseReview bool `json:"caseReview"`
+	Plans      int     `json:"plans"`
+	Releases   int     `json:"releases"`
+	Builds     int     `json:"builds"`
+	Cases      int     `json:"cases"`
+	Projects   int     `json:"projects"`
+	Executions int     `json:"executions"`
+	Bugs       int     `json:"bugs"`
+	Docs       int     `json:"docs"`
+	Progress   float64 `json:"progress"`
+	CaseReview bool    `json:"caseReview"`
 }
 
 type ZentaoProduct struct {
-	ConnectionId   uint64 `gorm:"primaryKey"`
-	Id             uint64 `json:"id" gorm:"primaryKey"`
+	ConnectionId   uint64 `gorm:"primaryKey;type:BIGINT  NOT NULL"`
+	Id             uint64 `json:"id" gorm:"primaryKey;type:BIGINT  NOT NULL"`
 	Program        int    `json:"program"`
 	Name           string `json:"name"`
 	Code           string `json:"code"`
@@ -116,7 +116,7 @@ type ZentaoProduct struct {
 	Executions     int        `json:"executions"`
 	Bugs           int        `json:"bugs"`
 	Docs           int        `json:"docs"`
-	Progress       int        `json:"progress"`
+	Progress       float64    `json:"progress"`
 	CaseReview     bool       `json:"caseReview"`
 	common.NoPKModel
 }
diff --git a/plugins/zentao/models/project.go b/plugins/zentao/models/project.go
index 08238a8a5..1d6627a1a 100644
--- a/plugins/zentao/models/project.go
+++ b/plugins/zentao/models/project.go
@@ -82,11 +82,11 @@ type ZentaoProject struct {
 	TeamCount      int    `json:"teamCount"`
 	LeftTasks      string `json:"leftTasks"`
 	//TeamMembers   []interface{} `json:"teamMembers" gorm:"-"`
-	TotalEstimate int `json:"totalEstimate"`
-	TotalConsumed int `json:"totalConsumed"`
-	TotalLeft     int `json:"totalLeft"`
-	Progress      int `json:"progress"`
-	TotalReal     int `json:"totalReal"`
+	TotalEstimate int     `json:"totalEstimate"`
+	TotalConsumed int     `json:"totalConsumed"`
+	TotalLeft     int     `json:"totalLeft"`
+	Progress      float64 `json:"progress"`
+	TotalReal     int     `json:"totalReal"`
 }
 type PM struct {
 	PmId       uint64 `json:"id"`
diff --git a/plugins/zentao/models/task.go b/plugins/zentao/models/task.go
index 4ca8777c5..228e38a7a 100644
--- a/plugins/zentao/models/task.go
+++ b/plugins/zentao/models/task.go
@@ -106,7 +106,7 @@ type ZentaoTaskRes struct {
 	Repo           int        `json:"repo"`
 	Mr             int        `json:"mr"`
 	Entry          string     `json:"entry"`
-	Lines          string     `json:"lines"`
+	NumOfLine      string     `json:"lines"`
 	V1             string     `json:"v1"`
 	V2             string     `json:"v2"`
 	Deleted        bool       `json:"deleted"`
@@ -119,11 +119,11 @@ type ZentaoTaskRes struct {
 	} `json:"latestStoryVersion"`
 	StoryStatus interface {
 	} `json:"storyStatus"`
-	AssignedToRealName string `json:"assignedToRealName"`
-	PriOrder           string `json:"priOrder"`
-	Delay              int    `json:"delay"`
-	NeedConfirm        bool   `json:"needConfirm"`
-	Progress           int    `json:"progress"`
+	AssignedToRealName string  `json:"assignedToRealName"`
+	PriOrder           string  `json:"priOrder"`
+	Delay              int     `json:"delay"`
+	NeedConfirm        bool    `json:"needConfirm"`
+	Progress           float64 `json:"progress"`
 }
 
 type ZentaoTask struct {
@@ -180,7 +180,7 @@ type ZentaoTask struct {
 	Repo               int        `json:"repo"`
 	Mr                 int        `json:"mr"`
 	Entry              string     `json:"entry"`
-	Lines              string     `json:"lines"`
+	NumOfLine          string     `json:"lines"`
 	V1                 string     `json:"v1"`
 	V2                 string     `json:"v2"`
 	Deleted            bool       `json:"deleted"`
@@ -193,7 +193,7 @@ type ZentaoTask struct {
 	AssignedToRealName string     `json:"assignedToRealName"`
 	PriOrder           string     `json:"priOrder"`
 	NeedConfirm        bool       `json:"needConfirm"`
-	Progress           int        `json:"progress"`
+	Progress           float64    `json:"progress"`
 }
 
 func (ZentaoTask) TableName() string {
diff --git a/plugins/zentao/tasks/bug_extractor.go b/plugins/zentao/tasks/bug_extractor.go
index 088c75602..085e2a342 100644
--- a/plugins/zentao/tasks/bug_extractor.go
+++ b/plugins/zentao/tasks/bug_extractor.go
@@ -109,7 +109,7 @@ func ExtractBug(taskCtx core.SubTaskContext) errors.Error {
 				Repo:           res.Repo,
 				Mr:             res.Mr,
 				Entry:          res.Entry,
-				Lines:          res.Lines,
+				NumOfLine:      res.NumOfLine,
 				V1:             res.V1,
 				V2:             res.V2,
 				RepoType:       res.RepoType,
diff --git a/plugins/zentao/tasks/task_extractor.go b/plugins/zentao/tasks/task_extractor.go
index d736bc008..7fea9f0d6 100644
--- a/plugins/zentao/tasks/task_extractor.go
+++ b/plugins/zentao/tasks/task_extractor.go
@@ -106,7 +106,7 @@ func ExtractTask(taskCtx core.SubTaskContext) errors.Error {
 				Repo:               res.Repo,
 				Mr:                 res.Mr,
 				Entry:              res.Entry,
-				Lines:              res.Lines,
+				NumOfLine:          res.NumOfLine,
 				V1:                 res.V1,
 				V2:                 res.V2,
 				Deleted:            res.Deleted,