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

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


The following commit(s) were added to refs/heads/main by this push:
     new 1011c5cc9 feat: support multiple assignees for issues (#5321)
1011c5cc9 is described below

commit 1011c5cc9340f73c7c621dd60a7dbc932a7101eb
Author: Liang Zhang <[email protected]>
AuthorDate: Thu Jun 1 14:07:36 2023 +0800

    feat: support multiple assignees for issues (#5321)
    
    * feat: support multiple assignees for issue
    
    * fix: gitlab e2e
---
 backend/plugins/bitbucket/e2e/issue_test.go        |  7 +-
 .../e2e/snapshot_tables/issue_assignees.csv        | 13 ++++
 .../bitbucket/e2e/snapshot_tables/issues.csv       | 36 +++++-----
 backend/plugins/bitbucket/tasks/issue_convertor.go | 18 +++--
 backend/plugins/gitee/tasks/issue_convertor.go     | 25 +++++--
 backend/plugins/github/e2e/issue_assignee_test.go  | 49 +++++++++++++
 backend/plugins/github/e2e/issue_test.go           |  1 +
 .../_tool_github_issue_assignees.csv               | 10 +++
 .../github/e2e/snapshot_tables/issue_assignees.csv | 10 +++
 backend/plugins/github/impl/impl.go                |  1 +
 .../register.go => issue_assignee.go}              | 38 ++++------
 .../20230530_add_issue_assignee.go                 | 52 ++++++++++++++
 .../github/models/migrationscripts/register.go     |  1 +
 .../github/tasks/issue_assignee_convertor.go       | 83 ++++++++++++++++++++++
 backend/plugins/github/tasks/issue_extractor.go    | 13 +++-
 .../github_graphql/tasks/issue_collector.go        | 10 +++
 backend/plugins/gitlab/e2e/issues_test.go          |  1 +
 .../_tool_gitlab_issue_assignees.csv               |  6 ++
 .../gitlab/e2e/snapshot_tables/issue_assignees.csv |  6 ++
 backend/plugins/gitlab/impl/impl.go                |  1 +
 .../models/issue_assignee.go}                      | 38 ++++------
 .../gitlab/tasks/issue_assignee_convertor.go       | 75 +++++++++++++++++++
 backend/plugins/gitlab/tasks/issue_extractor.go    | 11 ++-
 backend/plugins/jira/e2e/issue_test.go             |  6 ++
 .../jira/e2e/snapshot_tables/issue_assignees.csv   | 31 ++++++++
 backend/plugins/jira/tasks/issue_convertor.go      | 14 ++--
 backend/plugins/pagerduty/e2e/incident_test.go     |  5 ++
 .../e2e/snapshot_tables/issue_assignees.csv        |  4 ++
 .../plugins/pagerduty/tasks/incidents_converter.go | 14 ++--
 backend/plugins/tapd/e2e/bugs_test.go              |  5 ++
 .../e2e/snapshot_tables/bug_issue_assignees.csv    | 10 +++
 .../e2e/snapshot_tables/story_issue_assignees.csv  | 21 ++++++
 .../e2e/snapshot_tables/task_issue_assignees.csv   | 16 +++++
 backend/plugins/tapd/e2e/stories_test.go           |  6 ++
 backend/plugins/tapd/e2e/tasks_test.go             |  6 ++
 backend/plugins/tapd/tasks/bug_converter.go        |  8 ++-
 backend/plugins/tapd/tasks/story_converter.go      |  8 ++-
 backend/plugins/tapd/tasks/task_converter.go       |  8 ++-
 backend/plugins/zentao/e2e/bug_test.go             |  8 ++-
 .../e2e/snapshot_tables/bug_issue_assignees.csv    |  5 ++
 .../e2e/snapshot_tables/story_issue_assignees.csv  |  8 +++
 .../e2e/snapshot_tables/task_issue_assignees.csv   |  4 ++
 backend/plugins/zentao/e2e/story_test.go           |  8 ++-
 backend/plugins/zentao/e2e/task_test.go            |  8 ++-
 backend/plugins/zentao/tasks/bug_convertor.go      | 15 +++-
 backend/plugins/zentao/tasks/story_convertor.go    | 10 ++-
 backend/plugins/zentao/tasks/task_convertor.go     | 10 ++-
 47 files changed, 639 insertions(+), 104 deletions(-)

diff --git a/backend/plugins/bitbucket/e2e/issue_test.go 
b/backend/plugins/bitbucket/e2e/issue_test.go
index cae94e33c..2013e6286 100644
--- a/backend/plugins/bitbucket/e2e/issue_test.go
+++ b/backend/plugins/bitbucket/e2e/issue_test.go
@@ -20,6 +20,7 @@ package e2e
 import (
        "testing"
 
+       "github.com/apache/incubator-devlake/core/models/common"
        "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
        "github.com/apache/incubator-devlake/helpers/e2ehelper"
        "github.com/apache/incubator-devlake/plugins/bitbucket/impl"
@@ -96,6 +97,7 @@ func TestIssueDataFlow(t *testing.T) {
        // verify issue conversion
        dataflowTester.FlushTabler(&ticket.Issue{})
        dataflowTester.FlushTabler(&ticket.BoardIssue{})
+       dataflowTester.FlushTabler(&ticket.IssueAssignee{})
        dataflowTester.Subtask(tasks.ConvertIssuesMeta, taskData)
        dataflowTester.VerifyTable(
                ticket.Issue{},
@@ -135,5 +137,8 @@ func TestIssueDataFlow(t *testing.T) {
                        "issue_id",
                ),
        )
-
+       dataflowTester.VerifyTableWithOptions(ticket.IssueAssignee{}, 
e2ehelper.TableOptions{
+               CSVRelPath:  "./snapshot_tables/issue_assignees.csv",
+               IgnoreTypes: []interface{}{common.NoPKModel{}},
+       })
 }
diff --git a/backend/plugins/bitbucket/e2e/snapshot_tables/issue_assignees.csv 
b/backend/plugins/bitbucket/e2e/snapshot_tables/issue_assignees.csv
new file mode 100644
index 000000000..0bbdfa965
--- /dev/null
+++ b/backend/plugins/bitbucket/e2e/snapshot_tables/issue_assignees.csv
@@ -0,0 +1,13 @@
+issue_id,assignee_id,assignee_name
+bitbucket:BitbucketIssue:1:likyh/likyhphp:1,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe
+bitbucket:BitbucketIssue:1:likyh/likyhphp:10,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe
+bitbucket:BitbucketIssue:1:likyh/likyhphp:11,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe
+bitbucket:BitbucketIssue:1:likyh/likyhphp:14,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe
+bitbucket:BitbucketIssue:1:likyh/likyhphp:19,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe
+bitbucket:BitbucketIssue:1:likyh/likyhphp:20,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe
+bitbucket:BitbucketIssue:1:likyh/likyhphp:23,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe
+bitbucket:BitbucketIssue:1:likyh/likyhphp:24,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe
+bitbucket:BitbucketIssue:1:likyh/likyhphp:29,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe
+bitbucket:BitbucketIssue:1:likyh/likyhphp:30,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe
+bitbucket:BitbucketIssue:1:likyh/likyhphp:6,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe
+bitbucket:BitbucketIssue:1:likyh/likyhphp:9,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe
diff --git a/backend/plugins/bitbucket/e2e/snapshot_tables/issues.csv 
b/backend/plugins/bitbucket/e2e/snapshot_tables/issues.csv
index 4ab51932a..69e58dafb 100644
--- a/backend/plugins/bitbucket/e2e/snapshot_tables/issues.csv
+++ b/backend/plugins/bitbucket/e2e/snapshot_tables/issues.csv
@@ -2,18 +2,18 @@ 
id,url,icon_url,issue_key,title,description,epic_key,type,status,original_status
 
bitbucket:BitbucketIssue:1:likyh/likyhphp:1,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/1,,1,issue
 test,bitbucket issues test for 
devants,,issue,TODO,new,0,,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,60,
 
bitbucket:BitbucketIssue:1:likyh/likyhphp:10,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/10,,10,issue
 test007,issue 
test007,,issue,TODO,new,0,,0,,trivial,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,52,
 
bitbucket:BitbucketIssue:1:likyh/likyhphp:11,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/11,,11,issue
 test008,issue 
test008,,issue,TODO,new,0,,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,51,
-bitbucket:BitbucketIssue:1:likyh/likyhphp:12,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/12,,12,issue
 test009,issue 
test009,,issue,TODO,new,0,,0,,minor,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,50,
-bitbucket:BitbucketIssue:1:likyh/likyhphp:13,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/13,,13,issue
 test010,issue 
test010,,issue,TODO,new,0,,0,,critical,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,49,
+bitbucket:BitbucketIssue:1:likyh/likyhphp:12,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/12,,12,issue
 test009,issue 
test009,,issue,TODO,new,0,,0,,minor,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,"",,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,50,
+bitbucket:BitbucketIssue:1:likyh/likyhphp:13,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/13,,13,issue
 test010,issue 
test010,,issue,TODO,new,0,,0,,critical,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,"",,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,49,
 
bitbucket:BitbucketIssue:1:likyh/likyhphp:14,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/14,,14,issue
 test011,issue 
test011,,issue,TODO,new,0,,0,,blocker,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,48,
-bitbucket:BitbucketIssue:1:likyh/likyhphp:15,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/15,,15,issue
 test012,issue 
test012,,issue,TODO,new,0,,0,,minor,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,47,
-bitbucket:BitbucketIssue:1:likyh/likyhphp:16,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/16,,16,issue
 test013,issue 
test013,,issue,TODO,new,0,,0,,trivial,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,46,
-bitbucket:BitbucketIssue:1:likyh/likyhphp:17,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/17,,17,issue
 test014,issue 
test014,,issue,TODO,new,0,,0,,trivial,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,45,
-bitbucket:BitbucketIssue:1:likyh/likyhphp:18,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/18,,18,issue
 test015,issue 
test015,,issue,TODO,new,0,,0,,minor,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,44,
+bitbucket:BitbucketIssue:1:likyh/likyhphp:15,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/15,,15,issue
 test012,issue 
test012,,issue,TODO,new,0,,0,,minor,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,"",,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,47,
+bitbucket:BitbucketIssue:1:likyh/likyhphp:16,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/16,,16,issue
 test013,issue 
test013,,issue,TODO,new,0,,0,,trivial,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,"",,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,46,
+bitbucket:BitbucketIssue:1:likyh/likyhphp:17,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/17,,17,issue
 test014,issue 
test014,,issue,TODO,new,0,,0,,trivial,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,"",,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,45,
+bitbucket:BitbucketIssue:1:likyh/likyhphp:18,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/18,,18,issue
 test015,issue 
test015,,issue,TODO,new,0,,0,,minor,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,"",,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,44,
 
bitbucket:BitbucketIssue:1:likyh/likyhphp:19,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/19,,19,issue
 test016,issue 
test016,,issue,TODO,new,0,,0,,critical,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,43,
-bitbucket:BitbucketIssue:1:likyh/likyhphp:2,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/2,,2,add
 bitbucket 
issue,feafejo,,issue,TODO,new,0,,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,59,
+bitbucket:BitbucketIssue:1:likyh/likyhphp:2,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/2,,2,add
 bitbucket 
issue,feafejo,,issue,TODO,new,0,,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,"",,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,59,
 
bitbucket:BitbucketIssue:1:likyh/likyhphp:20,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/20,,20,issue
 test017,issue 
test017,,issue,TODO,new,0,,0,,blocker,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,42,
-bitbucket:BitbucketIssue:1:likyh/likyhphp:21,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/21,,21,issue
 test018,issue 
test018,,issue,TODO,new,0,,0,,trivial,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,41,
-bitbucket:BitbucketIssue:1:likyh/likyhphp:22,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/22,,22,issue
 test019,issue 
test019,,issue,TODO,new,0,,0,,minor,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,40,
+bitbucket:BitbucketIssue:1:likyh/likyhphp:21,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/21,,21,issue
 test018,issue 
test018,,issue,TODO,new,0,,0,,trivial,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,"",,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,41,
+bitbucket:BitbucketIssue:1:likyh/likyhphp:22,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/22,,22,issue
 test019,issue 
test019,,issue,TODO,new,0,,0,,minor,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,"",,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,40,
 
bitbucket:BitbucketIssue:1:likyh/likyhphp:23,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/23,,23,issue
 test020,issue 
test020,,issue,TODO,new,0,,0,,critical,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,39,
 
bitbucket:BitbucketIssue:1:likyh/likyhphp:24,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/24,,24,issue
 test021,"issue test021 
ijeiawgoeive/faveevaeviaevfejaofejfioejaiofe_veavejiovajgiorejoifjrogiorejieafajejaojoejvgioriovioraivjairobnrnoivaiorjbiorjiojaeiorjvioejroivjaoijeriojiaojioeefjafioejfiojeiofawefwefoiwefiwoiefweefwoefuwhufirfrw._
 
@@ -38,18 +38,18 @@ feaofe
 |  |  |  |
 |  |  |  |
 
-‌",,issue,TODO,new,0,,0,,critical,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,36,
-bitbucket:BitbucketIssue:1:likyh/likyhphp:26,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/26,,26,issue
 test022,issue 
test022,,issue,TODO,new,0,,0,,blocker,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,35,
+‌",,issue,TODO,new,0,,0,,critical,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,"",,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,36,
+bitbucket:BitbucketIssue:1:likyh/likyhphp:26,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/26,,26,issue
 test022,issue 
test022,,issue,TODO,new,0,,0,,blocker,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,"",,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,35,
 
bitbucket:BitbucketIssue:1:likyh/likyhphp:27,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/27,,27,issue
 test024,"issue test024v  aejnoafoeiogoiae
 
-qwofjeoiwjf",,issue,TODO,new,0,,0,,trivial,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,31,
-bitbucket:BitbucketIssue:1:likyh/likyhphp:28,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/28,,28,issue
 test025,issue 
test025,,issue,TODO,new,0,,0,,minor,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,34,
+qwofjeoiwjf",,issue,TODO,new,0,,0,,trivial,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,"",,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,31,
+bitbucket:BitbucketIssue:1:likyh/likyhphp:28,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/28,,28,issue
 test025,issue 
test025,,issue,TODO,new,0,,0,,minor,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,"",,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,34,
 
bitbucket:BitbucketIssue:1:likyh/likyhphp:29,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/29,,29,issue
 test026,issue 
test026,,issue,TODO,new,0,,0,,critical,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,33,
-bitbucket:BitbucketIssue:1:likyh/likyhphp:3,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/3,,3,bitbucket
 test,"efaegjeoaijefioaegrjoeior,af enfaoiee 
vioea.,,.wew",,issue,TODO,new,0,,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,58,
+bitbucket:BitbucketIssue:1:likyh/likyhphp:3,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/3,,3,bitbucket
 test,"efaegjeoaijefioaegrjoeior,af enfaoiee 
vioea.,,.wew",,issue,TODO,new,0,,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,"",,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,58,
 
bitbucket:BitbucketIssue:1:likyh/likyhphp:30,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/30,,30,issue
 test027,issue 
test027,,issue,TODO,new,0,,0,,critical,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,32,
-bitbucket:BitbucketIssue:1:likyh/likyhphp:4,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/4,,4,issue
 test001,Bitbucket issue 
test001,,issue,TODO,new,0,,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,57,
-bitbucket:BitbucketIssue:1:likyh/likyhphp:5,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/5,,5,issue
 test002,issue 
test002,,issue,TODO,new,0,,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,56,
+bitbucket:BitbucketIssue:1:likyh/likyhphp:4,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/4,,4,issue
 test001,Bitbucket issue 
test001,,issue,TODO,new,0,,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,"",,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,57,
+bitbucket:BitbucketIssue:1:likyh/likyhphp:5,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/5,,5,issue
 test002,issue 
test002,,issue,TODO,new,0,,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,"",,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,56,
 
bitbucket:BitbucketIssue:1:likyh/likyhphp:6,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/6,,6,issue
 test003,issue test 
003,,issue,TODO,new,0,,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,37,
-bitbucket:BitbucketIssue:1:likyh/likyhphp:7,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/7,,7,issue
 test004,issue 
test004,,issue,TODO,new,0,,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,55,
-bitbucket:BitbucketIssue:1:likyh/likyhphp:8,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/8,,8,issue
 test005,issue 
test005,,issue,TODO,new,0,,0,,critical,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,54,
+bitbucket:BitbucketIssue:1:likyh/likyhphp:7,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/7,,7,issue
 test004,issue 
test004,,issue,TODO,new,0,,0,,major,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,"",,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,55,
+bitbucket:BitbucketIssue:1:likyh/likyhphp:8,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/8,,8,issue
 test005,issue 
test005,,issue,TODO,new,0,,0,,critical,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,"",,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,54,
 
bitbucket:BitbucketIssue:1:likyh/likyhphp:9,https://api.bitbucket.org/2.0/repositories/likyh/likyhphp/issues/9,,9,issue
 test006,issue 
test006,,issue,TODO,new,0,,0,,blocker,0,0,0,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,bitbucket:BitbucketAccount:1:62abf394192edb006fa0e8cf,teoiaoe,,,"{""ConnectionId"":1,""FullName"":""likyh/likyhphp""}",_raw_bitbucket_api_issues,53,
diff --git a/backend/plugins/bitbucket/tasks/issue_convertor.go 
b/backend/plugins/bitbucket/tasks/issue_convertor.go
index 4975c82ce..04c4f599b 100644
--- a/backend/plugins/bitbucket/tasks/issue_convertor.go
+++ b/backend/plugins/bitbucket/tasks/issue_convertor.go
@@ -80,22 +80,28 @@ func ConvertIssues(taskCtx plugin.SubTaskContext) 
errors.Error {
                                Severity:        issue.Severity,
                                Component:       issue.Component,
                        }
-                       if issue.AssigneeName != "" {
+                       var result []interface{}
+                       if issue.AssigneeId != "" {
                                domainIssue.AssigneeName = issue.AssigneeName
                                domainIssue.AssigneeId = 
accountIdGen.Generate(data.Options.ConnectionId, issue.AssigneeId)
+                               issueAssignee := &ticket.IssueAssignee{
+                                       IssueId:      domainIssue.Id,
+                                       AssigneeId:   domainIssue.AssigneeId,
+                                       AssigneeName: domainIssue.AssigneeName,
+                               }
+                               result = append(result, issueAssignee)
                        }
-                       if issue.AuthorName != "" {
+                       if issue.AuthorId != "" {
                                domainIssue.CreatorName = issue.AuthorName
                                domainIssue.CreatorId = 
accountIdGen.Generate(data.Options.ConnectionId, issue.AuthorId)
                        }
+                       result = append(result, domainIssue)
                        boardIssue := &ticket.BoardIssue{
                                BoardId: 
boardIdGen.Generate(data.Options.ConnectionId, repoId),
                                IssueId: domainIssue.Id,
                        }
-                       return []interface{}{
-                               domainIssue,
-                               boardIssue,
-                       }, nil
+                       result = append(result, boardIssue)
+                       return result, nil
                },
        })
        if err != nil {
diff --git a/backend/plugins/gitee/tasks/issue_convertor.go 
b/backend/plugins/gitee/tasks/issue_convertor.go
index 8c3890fb7..8cf0f86d3 100644
--- a/backend/plugins/gitee/tasks/issue_convertor.go
+++ b/backend/plugins/gitee/tasks/issue_convertor.go
@@ -18,6 +18,8 @@ limitations under the License.
 package tasks
 
 import (
+       "reflect"
+
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/models/domainlayer"
@@ -26,7 +28,6 @@ import (
        "github.com/apache/incubator-devlake/core/plugin"
        helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/gitee/models"
-       "reflect"
 )
 
 var ConvertIssuesMeta = plugin.SubTaskMeta{
@@ -70,9 +71,7 @@ func ConvertIssues(taskCtx plugin.SubTaskContext) 
errors.Error {
                                Description:     issue.Body,
                                Priority:        issue.Priority,
                                Type:            issue.Type,
-                               AssigneeId:      
accountIdGen.Generate(data.Options.ConnectionId, issue.AssigneeId),
                                AssigneeName:    issue.AssigneeName,
-                               CreatorId:       
accountIdGen.Generate(data.Options.ConnectionId, issue.AuthorId),
                                CreatorName:     issue.AuthorName,
                                LeadTimeMinutes: int64(issue.LeadTimeMinutes),
                                Url:             issue.Url,
@@ -82,19 +81,31 @@ func ConvertIssues(taskCtx plugin.SubTaskContext) 
errors.Error {
                                Severity:        issue.Severity,
                                Component:       issue.Component,
                        }
+                       var result []interface{}
+                       if issue.AssigneeId != 0 {
+                               domainIssue.AssigneeId = 
accountIdGen.Generate(data.Options.ConnectionId, issue.AssigneeId)
+                               issueAssignee := &ticket.IssueAssignee{
+                                       IssueId:      domainIssue.Id,
+                                       AssigneeId:   domainIssue.AssigneeId,
+                                       AssigneeName: domainIssue.AssigneeName,
+                               }
+                               result = append(result, issueAssignee)
+                       }
+                       if issue.AuthorId != 0 {
+                               domainIssue.CreatorId = 
accountIdGen.Generate(data.Options.ConnectionId, issue.AuthorId)
+                       }
                        if issue.State == "closed" {
                                domainIssue.Status = ticket.DONE
                        } else {
                                domainIssue.Status = ticket.TODO
                        }
+                       result = append(result, domainIssue)
                        boardIssue := &ticket.BoardIssue{
                                BoardId: 
boardIdGen.Generate(data.Options.ConnectionId, repoId),
                                IssueId: domainIssue.Id,
                        }
-                       return []interface{}{
-                               domainIssue,
-                               boardIssue,
-                       }, nil
+                       result = append(result, boardIssue)
+                       return result, nil
                },
        })
        if err != nil {
diff --git a/backend/plugins/github/e2e/issue_assignee_test.go 
b/backend/plugins/github/e2e/issue_assignee_test.go
new file mode 100644
index 000000000..423d6a12a
--- /dev/null
+++ b/backend/plugins/github/e2e/issue_assignee_test.go
@@ -0,0 +1,49 @@
+/*
+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 e2e
+
+import (
+       "github.com/apache/incubator-devlake/plugins/github/models"
+       "testing"
+
+       "github.com/apache/incubator-devlake/core/models/common"
+       "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+       "github.com/apache/incubator-devlake/helpers/e2ehelper"
+       "github.com/apache/incubator-devlake/plugins/github/impl"
+       "github.com/apache/incubator-devlake/plugins/github/tasks"
+)
+
+func TestIssueAssigneeDataFlow(t *testing.T) {
+       var plugin impl.Github
+       dataflowTester := e2ehelper.NewDataFlowTester(t, "github", plugin)
+
+       taskData := &tasks.GithubTaskData{
+               Options: &tasks.GithubOptions{
+                       ConnectionId: 1,
+                       GithubId:     1,
+               },
+       }
+
+       
dataflowTester.ImportCsvIntoTabler("./snapshot_tables/_tool_github_issue_assignees.csv",
 &models.GithubIssueAssignee{})
+       dataflowTester.FlushTabler(&ticket.IssueAssignee{})
+       dataflowTester.Subtask(tasks.ConvertIssueAssigneeMeta, taskData)
+       dataflowTester.VerifyTableWithOptions(ticket.IssueAssignee{}, 
e2ehelper.TableOptions{
+               CSVRelPath:  "./snapshot_tables/issue_assignees.csv",
+               IgnoreTypes: []interface{}{common.NoPKModel{}},
+       })
+}
diff --git a/backend/plugins/github/e2e/issue_test.go 
b/backend/plugins/github/e2e/issue_test.go
index d3d838c2a..7c001c919 100644
--- a/backend/plugins/github/e2e/issue_test.go
+++ b/backend/plugins/github/e2e/issue_test.go
@@ -57,6 +57,7 @@ func TestIssueDataFlow(t *testing.T) {
        dataflowTester.FlushTabler(&models.GithubIssue{})
        dataflowTester.FlushTabler(&models.GithubIssueLabel{})
        dataflowTester.FlushTabler(&models.GithubRepoAccount{})
+       dataflowTester.FlushTabler(&models.GithubIssueAssignee{})
        dataflowTester.Subtask(tasks.ExtractApiIssuesMeta, taskData)
        dataflowTester.VerifyTable(
                models.GithubIssue{},
diff --git 
a/backend/plugins/github/e2e/snapshot_tables/_tool_github_issue_assignees.csv 
b/backend/plugins/github/e2e/snapshot_tables/_tool_github_issue_assignees.csv
new file mode 100644
index 000000000..00df5a68a
--- /dev/null
+++ 
b/backend/plugins/github/e2e/snapshot_tables/_tool_github_issue_assignees.csv
@@ -0,0 +1,10 @@
+connection_id,issue_id,repo_id,assignee_id,assignee_name
+1,1,1,1,name1
+1,1,1,2,name2
+1,1,1,3,name3
+1,1,1,4,name4
+1,1,1,5,name5
+1,1,1,6,name6
+1,1,1,7,name7
+1,1,1,8,name8
+1,1,1,9,name9
\ No newline at end of file
diff --git a/backend/plugins/github/e2e/snapshot_tables/issue_assignees.csv 
b/backend/plugins/github/e2e/snapshot_tables/issue_assignees.csv
new file mode 100644
index 000000000..208c157c5
--- /dev/null
+++ b/backend/plugins/github/e2e/snapshot_tables/issue_assignees.csv
@@ -0,0 +1,10 @@
+issue_id,assignee_id,assignee_name
+github:GithubIssue:1:1,github:GithubAccount:1:1,name1
+github:GithubIssue:1:1,github:GithubAccount:1:2,name2
+github:GithubIssue:1:1,github:GithubAccount:1:3,name3
+github:GithubIssue:1:1,github:GithubAccount:1:4,name4
+github:GithubIssue:1:1,github:GithubAccount:1:5,name5
+github:GithubIssue:1:1,github:GithubAccount:1:6,name6
+github:GithubIssue:1:1,github:GithubAccount:1:7,name7
+github:GithubIssue:1:1,github:GithubAccount:1:8,name8
+github:GithubIssue:1:1,github:GithubAccount:1:9,name9
diff --git a/backend/plugins/github/impl/impl.go 
b/backend/plugins/github/impl/impl.go
index 606ffc8c3..224c3a5e2 100644
--- a/backend/plugins/github/impl/impl.go
+++ b/backend/plugins/github/impl/impl.go
@@ -128,6 +128,7 @@ func (p Github) SubTaskMetas() []plugin.SubTaskMeta {
                tasks.EnrichPullRequestIssuesMeta,
                tasks.ConvertRepoMeta,
                tasks.ConvertIssuesMeta,
+               tasks.ConvertIssueAssigneeMeta,
                tasks.ConvertCommitsMeta,
                tasks.ConvertIssueLabelsMeta,
                tasks.ConvertPullRequestCommitsMeta,
diff --git a/backend/plugins/github/models/migrationscripts/register.go 
b/backend/plugins/github/models/issue_assignee.go
similarity index 51%
copy from backend/plugins/github/models/migrationscripts/register.go
copy to backend/plugins/github/models/issue_assignee.go
index bf800e468..4998198f4 100644
--- a/backend/plugins/github/models/migrationscripts/register.go
+++ b/backend/plugins/github/models/issue_assignee.go
@@ -15,31 +15,19 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 */
 
-package migrationscripts
+package models
 
-import (
-       "github.com/apache/incubator-devlake/core/plugin"
-)
+import "github.com/apache/incubator-devlake/core/models/common"
 
-// All return all the migration scripts
-func All() []plugin.MigrationScript {
-       println("github register.go")
-       return []plugin.MigrationScript{
-               new(addInitTables),
-               new(addGithubRunsTable),
-               new(addGithubJobsTable),
-               new(addGithubPipelineTable),
-               new(deleteGithubPipelineTable),
-               new(addHeadRepoIdFieldInGithubPr),
-               new(addEnableGraphqlForConnection),
-               new(addTransformationRule20221124),
-               new(concatOwnerAndName),
-               new(addStdTypeToIssue221230),
-               new(addConnectionIdToTransformationRule),
-               new(addEnvToRunAndJob),
-               new(addGithubCommitAuthorInfo),
-               new(fixRunNameToText),
-               new(addGithubMultiAuth),
-               new(renameTr2ScopeConfig),
-       }
+type GithubIssueAssignee struct {
+       common.NoPKModel
+       ConnectionId uint64 `gorm:"primaryKey"`
+       IssueId      int    `gorm:"primaryKey"`
+       RepoId       int    `gorm:"primaryKey"`
+       AssigneeId   int    `gorm:"primaryKey"`
+       AssigneeName string `gorm:"type:varchar(255)"`
+}
+
+func (GithubIssueAssignee) TableName() string {
+       return "_tool_github_issue_assignees"
 }
diff --git 
a/backend/plugins/github/models/migrationscripts/20230530_add_issue_assignee.go 
b/backend/plugins/github/models/migrationscripts/20230530_add_issue_assignee.go
new file mode 100644
index 000000000..1a460b2f9
--- /dev/null
+++ 
b/backend/plugins/github/models/migrationscripts/20230530_add_issue_assignee.go
@@ -0,0 +1,52 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package migrationscripts
+
+import (
+       "github.com/apache/incubator-devlake/core/context"
+       "github.com/apache/incubator-devlake/core/errors"
+       
"github.com/apache/incubator-devlake/core/models/migrationscripts/archived"
+)
+
+type githubIssueAssignee20230530 struct {
+       archived.NoPKModel
+       ConnectionId uint64 `gorm:"primaryKey"`
+       IssueId      int    `gorm:"primaryKey"`
+       RepoId       int    `gorm:"primaryKey"`
+       AssigneeId   int    `gorm:"primaryKey"`
+       AssigneeName string `gorm:"type:varchar(255)"`
+}
+
+func (githubIssueAssignee20230530) TableName() string {
+       return "_tool_github_issue_assignees"
+}
+
+type addGithubIssueAssignee struct{}
+
+func (*addGithubIssueAssignee) Up(res context.BasicRes) errors.Error {
+       db := res.GetDal()
+       return db.AutoMigrate(&githubIssueAssignee20230530{})
+}
+
+func (*addGithubIssueAssignee) Version() uint64 {
+       return 20230530161510
+}
+
+func (*addGithubIssueAssignee) Name() string {
+       return "add _tool_github_issue_assignees table"
+}
diff --git a/backend/plugins/github/models/migrationscripts/register.go 
b/backend/plugins/github/models/migrationscripts/register.go
index bf800e468..518f347ac 100644
--- a/backend/plugins/github/models/migrationscripts/register.go
+++ b/backend/plugins/github/models/migrationscripts/register.go
@@ -41,5 +41,6 @@ func All() []plugin.MigrationScript {
                new(fixRunNameToText),
                new(addGithubMultiAuth),
                new(renameTr2ScopeConfig),
+               new(addGithubIssueAssignee),
        }
 }
diff --git a/backend/plugins/github/tasks/issue_assignee_convertor.go 
b/backend/plugins/github/tasks/issue_assignee_convertor.go
new file mode 100644
index 000000000..373bec2b5
--- /dev/null
+++ b/backend/plugins/github/tasks/issue_assignee_convertor.go
@@ -0,0 +1,83 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+       "reflect"
+
+       "github.com/apache/incubator-devlake/core/dal"
+       "github.com/apache/incubator-devlake/core/errors"
+       "github.com/apache/incubator-devlake/core/models/domainlayer/didgen"
+       "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+       "github.com/apache/incubator-devlake/core/plugin"
+       "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+       "github.com/apache/incubator-devlake/plugins/github/models"
+)
+
+var ConvertIssueAssigneeMeta = plugin.SubTaskMeta{
+       Name:             "convertIssueAssignee",
+       EntryPoint:       ConvertIssueAssignee,
+       EnabledByDefault: true,
+       Description:      "Convert tool layer table 
_tool_github_issue_assignees into  domain layer table issue_assignees",
+       DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ConvertIssueAssignee(taskCtx plugin.SubTaskContext) errors.Error {
+       db := taskCtx.GetDal()
+       data := taskCtx.GetData().(*GithubTaskData)
+       repoId := data.Options.GithubId
+
+       cursor, err := db.Cursor(
+               dal.From(&models.GithubIssueAssignee{}),
+               dal.Where("repo_id = ? and connection_id=?", repoId, 
data.Options.ConnectionId),
+       )
+       if err != nil {
+               return err
+       }
+       defer cursor.Close()
+
+       issueIdGen := didgen.NewDomainIdGenerator(&models.GithubIssue{})
+       accountIdGen := didgen.NewDomainIdGenerator(&models.GithubAccount{})
+
+       converter, err := api.NewDataConverter(api.DataConverterArgs{
+               RawDataSubTaskArgs: api.RawDataSubTaskArgs{
+                       Ctx: taskCtx,
+                       Params: GithubApiParams{
+                               ConnectionId: data.Options.ConnectionId,
+                               Name:         data.Options.Name,
+                       },
+                       Table: RAW_ISSUE_TABLE,
+               },
+               InputRowType: reflect.TypeOf(models.GithubIssueAssignee{}),
+               Input:        cursor,
+               Convert: func(inputRow interface{}) ([]interface{}, 
errors.Error) {
+                       githubIssueAssignee := 
inputRow.(*models.GithubIssueAssignee)
+                       issueAssignee := &ticket.IssueAssignee{
+                               IssueId:      
issueIdGen.Generate(data.Options.ConnectionId, githubIssueAssignee.IssueId),
+                               AssigneeId:   
accountIdGen.Generate(data.Options.ConnectionId, 
githubIssueAssignee.AssigneeId),
+                               AssigneeName: githubIssueAssignee.AssigneeName,
+                       }
+                       return []interface{}{issueAssignee}, nil
+               },
+       })
+       if err != nil {
+               return err
+       }
+
+       return converter.Execute()
+}
diff --git a/backend/plugins/github/tasks/issue_extractor.go 
b/backend/plugins/github/tasks/issue_extractor.go
index 7bc2a6f5e..29f6c43e2 100644
--- a/backend/plugins/github/tasks/issue_extractor.go
+++ b/backend/plugins/github/tasks/issue_extractor.go
@@ -51,7 +51,8 @@ type IssuesResponse struct {
        Labels []struct {
                Name string `json:"name"`
        } `json:"labels"`
-       Assignee  *GithubAccountResponse
+       Assignee  *GithubAccountResponse  `json:"assignee"`
+       Assignees []GithubAccountResponse `json:"assignees"`
        User      *GithubAccountResponse
        Milestone *struct {
                Id int
@@ -129,6 +130,16 @@ func ExtractApiIssues(taskCtx plugin.SubTaskContext) 
errors.Error {
                                }
                                results = append(results, relatedUser)
                        }
+                       for _, assignee := range body.Assignees {
+                               issueAssignee := &models.GithubIssueAssignee{
+                                       ConnectionId: githubIssue.ConnectionId,
+                                       IssueId:      githubIssue.GithubId,
+                                       RepoId:       githubIssue.RepoId,
+                                       AssigneeId:   assignee.Id,
+                                       AssigneeName: assignee.Login,
+                               }
+                               results = append(results, issueAssignee)
+                       }
                        if body.User != nil {
                                githubIssue.AuthorId = body.User.Id
                                githubIssue.AuthorName = body.User.Login
diff --git a/backend/plugins/github_graphql/tasks/issue_collector.go 
b/backend/plugins/github_graphql/tasks/issue_collector.go
index 535dc016e..3379d860b 100644
--- a/backend/plugins/github_graphql/tasks/issue_collector.go
+++ b/backend/plugins/github_graphql/tasks/issue_collector.go
@@ -171,6 +171,16 @@ func CollectIssue(taskCtx plugin.SubTaskContext) 
errors.Error {
                                        }
                                        results = append(results, relatedUser)
                                }
+                               for _, assignee := range 
issue.AssigneeList.Assignees {
+                                       issueAssignee := 
&models.GithubIssueAssignee{
+                                               ConnectionId: 
githubIssue.ConnectionId,
+                                               IssueId:      
githubIssue.GithubId,
+                                               RepoId:       
githubIssue.RepoId,
+                                               AssigneeId:   assignee.Id,
+                                               AssigneeName: assignee.Login,
+                                       }
+                                       results = append(results, issueAssignee)
+                               }
                        }
                        if isFinish {
                                return results, helper.ErrFinishCollect
diff --git a/backend/plugins/gitlab/e2e/issues_test.go 
b/backend/plugins/gitlab/e2e/issues_test.go
index 4f106742b..0553af3e8 100644
--- a/backend/plugins/gitlab/e2e/issues_test.go
+++ b/backend/plugins/gitlab/e2e/issues_test.go
@@ -47,6 +47,7 @@ func TestGitlabIssueDataFlow(t *testing.T) {
        dataflowTester.FlushTabler(&models.GitlabIssue{})
        dataflowTester.FlushTabler(&models.GitlabAccount{})
        dataflowTester.FlushTabler(&models.GitlabIssueLabel{})
+       dataflowTester.FlushTabler(&models.GitlabIssueAssignee{})
        dataflowTester.Subtask(tasks.ExtractApiIssuesMeta, taskData)
        dataflowTester.VerifyTable(
                models.GitlabIssue{},
diff --git 
a/backend/plugins/gitlab/e2e/snapshot_tables/_tool_gitlab_issue_assignees.csv 
b/backend/plugins/gitlab/e2e/snapshot_tables/_tool_gitlab_issue_assignees.csv
new file mode 100644
index 000000000..924b48a60
--- /dev/null
+++ 
b/backend/plugins/gitlab/e2e/snapshot_tables/_tool_gitlab_issue_assignees.csv
@@ -0,0 +1,6 @@
+connection_id,gitlab_id,project_id,assignee_id,assignee_name
+1,22097949,12345678,2295562,emilie
+1,24172103,12345678,5212782,m_walker
+1,32460839,12345678,2295562,emilie
+1,33004486,12345678,4189780,mpeychet_
+1,108497826,12345678,9386100,chrissharp
diff --git a/backend/plugins/gitlab/e2e/snapshot_tables/issue_assignees.csv 
b/backend/plugins/gitlab/e2e/snapshot_tables/issue_assignees.csv
new file mode 100644
index 000000000..66826f9c6
--- /dev/null
+++ b/backend/plugins/gitlab/e2e/snapshot_tables/issue_assignees.csv
@@ -0,0 +1,6 @@
+issue_id,assignee_id,assignee_name
+gitlab:GitlabIssue:1:108497826,gitlab:GitlabAccount:1:9386100,chrissharp
+gitlab:GitlabIssue:1:22097949,gitlab:GitlabAccount:1:2295562,emilie
+gitlab:GitlabIssue:1:24172103,gitlab:GitlabAccount:1:5212782,m_walker
+gitlab:GitlabIssue:1:32460839,gitlab:GitlabAccount:1:2295562,emilie
+gitlab:GitlabIssue:1:33004486,gitlab:GitlabAccount:1:4189780,mpeychet_
diff --git a/backend/plugins/gitlab/impl/impl.go 
b/backend/plugins/gitlab/impl/impl.go
index 4343b7a55..36fca3dec 100644
--- a/backend/plugins/gitlab/impl/impl.go
+++ b/backend/plugins/gitlab/impl/impl.go
@@ -120,6 +120,7 @@ func (p Gitlab) SubTaskMetas() []plugin.SubTaskMeta {
                tasks.ConvertMrCommentMeta,
                tasks.ConvertApiMrCommitsMeta,
                tasks.ConvertIssuesMeta,
+               tasks.ConvertIssueAssigneeMeta,
                tasks.ConvertIssueLabelsMeta,
                tasks.ConvertMrLabelsMeta,
                tasks.ConvertCommitsMeta,
diff --git a/backend/plugins/github/models/migrationscripts/register.go 
b/backend/plugins/gitlab/models/issue_assignee.go
similarity index 51%
copy from backend/plugins/github/models/migrationscripts/register.go
copy to backend/plugins/gitlab/models/issue_assignee.go
index bf800e468..d00c3cc68 100644
--- a/backend/plugins/github/models/migrationscripts/register.go
+++ b/backend/plugins/gitlab/models/issue_assignee.go
@@ -15,31 +15,19 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 */
 
-package migrationscripts
+package models
 
-import (
-       "github.com/apache/incubator-devlake/core/plugin"
-)
+import "github.com/apache/incubator-devlake/core/models/common"
 
-// All return all the migration scripts
-func All() []plugin.MigrationScript {
-       println("github register.go")
-       return []plugin.MigrationScript{
-               new(addInitTables),
-               new(addGithubRunsTable),
-               new(addGithubJobsTable),
-               new(addGithubPipelineTable),
-               new(deleteGithubPipelineTable),
-               new(addHeadRepoIdFieldInGithubPr),
-               new(addEnableGraphqlForConnection),
-               new(addTransformationRule20221124),
-               new(concatOwnerAndName),
-               new(addStdTypeToIssue221230),
-               new(addConnectionIdToTransformationRule),
-               new(addEnvToRunAndJob),
-               new(addGithubCommitAuthorInfo),
-               new(fixRunNameToText),
-               new(addGithubMultiAuth),
-               new(renameTr2ScopeConfig),
-       }
+type GitlabIssueAssignee struct {
+       common.NoPKModel
+       ConnectionId uint64 `gorm:"primaryKey"`
+       GitlabId     int    `gorm:"primaryKey"`
+       ProjectId    int    `gorm:"primaryKey"`
+       AssigneeId   int    `gorm:"primaryKey"`
+       AssigneeName string `gorm:"type:varchar(255)"`
+}
+
+func (GitlabIssueAssignee) TableName() string {
+       return "_tool_gitlab_issue_assignees"
 }
diff --git a/backend/plugins/gitlab/tasks/issue_assignee_convertor.go 
b/backend/plugins/gitlab/tasks/issue_assignee_convertor.go
new file mode 100644
index 000000000..9e7366e8b
--- /dev/null
+++ b/backend/plugins/gitlab/tasks/issue_assignee_convertor.go
@@ -0,0 +1,75 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package tasks
+
+import (
+       "reflect"
+
+       "github.com/apache/incubator-devlake/core/dal"
+       "github.com/apache/incubator-devlake/core/errors"
+       "github.com/apache/incubator-devlake/core/models/domainlayer/didgen"
+       "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
+       "github.com/apache/incubator-devlake/core/plugin"
+       helper "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
+       "github.com/apache/incubator-devlake/plugins/gitlab/models"
+)
+
+var ConvertIssueAssigneeMeta = plugin.SubTaskMeta{
+       Name:             "convertIssueAssignee",
+       EntryPoint:       ConvertIssueAssignee,
+       EnabledByDefault: true,
+       Description:      "Convert tool layer table 
_tool_gitlab_issue_assignees into  domain layer table issue_assignees",
+       DomainTypes:      []string{plugin.DOMAIN_TYPE_TICKET},
+}
+
+func ConvertIssueAssignee(taskCtx plugin.SubTaskContext) errors.Error {
+       db := taskCtx.GetDal()
+       rawDataSubTaskArgs, data := CreateRawDataSubTaskArgs(taskCtx, 
RAW_ISSUE_TABLE)
+
+       cursor, err := db.Cursor(
+               dal.From(&models.GitlabIssueAssignee{}),
+               dal.Where("project_id = ? and connection_id=?", 
data.Options.ProjectId, data.Options.ConnectionId),
+       )
+       if err != nil {
+               return err
+       }
+       defer cursor.Close()
+
+       issueIdGen := didgen.NewDomainIdGenerator(&models.GitlabIssue{})
+       accountIdGen := didgen.NewDomainIdGenerator(&models.GitlabAccount{})
+
+       converter, err := helper.NewDataConverter(helper.DataConverterArgs{
+               RawDataSubTaskArgs: *rawDataSubTaskArgs,
+               InputRowType:       
reflect.TypeOf(models.GitlabIssueAssignee{}),
+               Input:              cursor,
+               Convert: func(inputRow interface{}) ([]interface{}, 
errors.Error) {
+                       input := inputRow.(*models.GitlabIssueAssignee)
+                       domainIssueAssignee := &ticket.IssueAssignee{
+                               IssueId:      
issueIdGen.Generate(data.Options.ConnectionId, input.GitlabId),
+                               AssigneeId:   
accountIdGen.Generate(data.Options.ConnectionId, input.AssigneeId),
+                               AssigneeName: input.AssigneeName,
+                       }
+                       return []interface{}{domainIssueAssignee}, nil
+               },
+       })
+       if err != nil {
+               return err
+       }
+
+       return converter.Execute()
+}
diff --git a/backend/plugins/gitlab/tasks/issue_extractor.go 
b/backend/plugins/gitlab/tasks/issue_extractor.go
index 315200089..793c8edf2 100644
--- a/backend/plugins/gitlab/tasks/issue_extractor.go
+++ b/backend/plugins/gitlab/tasks/issue_extractor.go
@@ -256,7 +256,7 @@ func ExtractApiIssues(taskCtx plugin.SubTaskContext) 
errors.Error {
                        results = append(results, gitlabIssue)
 
                        for _, v := range body.Assignees {
-                               GitlabAssignee := &models.GitlabAccount{
+                               assignee := &models.GitlabAccount{
                                        ConnectionId: data.Options.ConnectionId,
                                        Username:     v.Username,
                                        Name:         v.Name,
@@ -264,7 +264,14 @@ func ExtractApiIssues(taskCtx plugin.SubTaskContext) 
errors.Error {
                                        AvatarUrl:    v.AvatarUrl,
                                        WebUrl:       v.WebUrl,
                                }
-                               results = append(results, GitlabAssignee)
+                               issueAssignee := &models.GitlabIssueAssignee{
+                                       ConnectionId: data.Options.ConnectionId,
+                                       GitlabId:     gitlabIssue.GitlabId,
+                                       ProjectId:    gitlabIssue.ProjectId,
+                                       AssigneeId:   v.Id,
+                                       AssigneeName: v.Username,
+                               }
+                               results = append(results, assignee, 
issueAssignee)
                        }
 
                        return results, nil
diff --git a/backend/plugins/jira/e2e/issue_test.go 
b/backend/plugins/jira/e2e/issue_test.go
index dec15eecb..6dfd8546e 100644
--- a/backend/plugins/jira/e2e/issue_test.go
+++ b/backend/plugins/jira/e2e/issue_test.go
@@ -20,6 +20,7 @@ package e2e
 import (
        "testing"
 
+       "github.com/apache/incubator-devlake/core/models/common"
        "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
        "github.com/apache/incubator-devlake/helpers/e2ehelper"
        "github.com/apache/incubator-devlake/plugins/jira/impl"
@@ -200,6 +201,7 @@ func TestIssueDataFlow(t *testing.T) {
        // verify issue conversion
        dataflowTester.FlushTabler(&ticket.Issue{})
        dataflowTester.FlushTabler(&ticket.BoardIssue{})
+       dataflowTester.FlushTabler(&ticket.IssueAssignee{})
        dataflowTester.Subtask(tasks.ConvertIssuesMeta, taskData)
        dataflowTester.VerifyTable(
                ticket.Issue{},
@@ -240,4 +242,8 @@ func TestIssueDataFlow(t *testing.T) {
                "./snapshot_tables/board_issues.csv",
                []string{"board_id", "issue_id"},
        )
+       dataflowTester.VerifyTableWithOptions(ticket.IssueAssignee{}, 
e2ehelper.TableOptions{
+               CSVRelPath:  "./snapshot_tables/issue_assignees.csv",
+               IgnoreTypes: []interface{}{common.NoPKModel{}},
+       })
 }
diff --git a/backend/plugins/jira/e2e/snapshot_tables/issue_assignees.csv 
b/backend/plugins/jira/e2e/snapshot_tables/issue_assignees.csv
new file mode 100644
index 000000000..44a39c887
--- /dev/null
+++ b/backend/plugins/jira/e2e/snapshot_tables/issue_assignees.csv
@@ -0,0 +1,31 @@
+issue_id,assignee_id,assignee_name
+jira:JiraIssue:2:10063,jira:JiraAccount:2:5ecfbd0c730ec90c1999cadf,
+jira:JiraIssue:2:10064,jira:JiraAccount:2:5ecfbd0c730ec90c1999cadf,
+jira:JiraIssue:2:10065,jira:JiraAccount:2:5ecfbd0c730ec90c1999cadf,
+jira:JiraIssue:2:10066,jira:JiraAccount:2:5ecfbd0c730ec90c1999cadf,
+jira:JiraIssue:2:10067,jira:JiraAccount:2:5ecfbd0c730ec90c1999cadf,
+jira:JiraIssue:2:10068,jira:JiraAccount:2:5ecfbd0c2490cf0c09e2e598,
+jira:JiraIssue:2:10070,jira:JiraAccount:2:5ecfbd0ba04d9c0c220c18d8,
+jira:JiraIssue:2:10071,jira:JiraAccount:2:5ecfbd0ba04d9c0c220c18d8,
+jira:JiraIssue:2:10072,jira:JiraAccount:2:5ecfbd0ba04d9c0c220c18d8,
+jira:JiraIssue:2:10076,jira:JiraAccount:2:5ecfbd0a47d31e0c2a15fd87,
+jira:JiraIssue:2:10077,jira:JiraAccount:2:5ecfbd0a47d31e0c2a15fd87,
+jira:JiraIssue:2:10078,jira:JiraAccount:2:5ecfbd0a47d31e0c2a15fd87,
+jira:JiraIssue:2:10079,jira:JiraAccount:2:5ecfbd0a47d31e0c2a15fd87,
+jira:JiraIssue:2:10081,jira:JiraAccount:2:5ecfbd0aaa47a00c1997ea8e,
+jira:JiraIssue:2:10082,jira:JiraAccount:2:5ecfbd0984083c0c12e5af8f,
+jira:JiraIssue:2:10085,jira:JiraAccount:2:5ecfbd0a47d31e0c2a15fd87,
+jira:JiraIssue:2:10086,jira:JiraAccount:2:5ecfbd0aaa47a00c1997ea8e,
+jira:JiraIssue:2:10087,jira:JiraAccount:2:5ecfbd0c730ec90c1999cadf,
+jira:JiraIssue:2:10088,jira:JiraAccount:2:5ecfbd0a47d31e0c2a15fd87,
+jira:JiraIssue:2:10089,jira:JiraAccount:2:5ecfbd0ba04d9c0c220c18d8,
+jira:JiraIssue:2:10090,jira:JiraAccount:2:5ecfbd0c730ec90c1999cadf,
+jira:JiraIssue:2:10091,jira:JiraAccount:2:5ecfbd0c730ec90c1999cadf,
+jira:JiraIssue:2:10092,jira:JiraAccount:2:5ecfbd0a47d31e0c2a15fd87,
+jira:JiraIssue:2:10093,jira:JiraAccount:2:5ecfbd0ba04d9c0c220c18d8,
+jira:JiraIssue:2:10094,jira:JiraAccount:2:5ecfbd0c730ec90c1999cadf,
+jira:JiraIssue:2:10095,jira:JiraAccount:2:5ecfbd0984083c0c12e5af8f,
+jira:JiraIssue:2:10096,jira:JiraAccount:2:5ecfbd0c730ec90c1999cadf,
+jira:JiraIssue:2:10097,jira:JiraAccount:2:5ecfbd0a47d31e0c2a15fd87,
+jira:JiraIssue:2:10098,jira:JiraAccount:2:5ecfbd0ba04d9c0c220c18d8,
+jira:JiraIssue:2:10099,jira:JiraAccount:2:5ecfbd0c730ec90c1999cadf,
diff --git a/backend/plugins/jira/tasks/issue_convertor.go 
b/backend/plugins/jira/tasks/issue_convertor.go
index ffdb0d02b..fa7e1227e 100644
--- a/backend/plugins/jira/tasks/issue_convertor.go
+++ b/backend/plugins/jira/tasks/issue_convertor.go
@@ -113,8 +113,15 @@ func ConvertIssues(taskCtx plugin.SubTaskContext) 
errors.Error {
                        if jiraIssue.CreatorDisplayName != "" {
                                issue.CreatorName = jiraIssue.CreatorDisplayName
                        }
+                       var result []interface{}
                        if jiraIssue.AssigneeAccountId != "" {
                                issue.AssigneeId = 
accountIdGen.Generate(data.Options.ConnectionId, jiraIssue.AssigneeAccountId)
+                               issueAssignee := &ticket.IssueAssignee{
+                                       IssueId:      issue.Id,
+                                       AssigneeId:   issue.AssigneeId,
+                                       AssigneeName: issue.AssigneeName,
+                               }
+                               result = append(result, issueAssignee)
                        }
                        if jiraIssue.AssigneeDisplayName != "" {
                                issue.AssigneeName = 
jiraIssue.AssigneeDisplayName
@@ -122,14 +129,13 @@ func ConvertIssues(taskCtx plugin.SubTaskContext) 
errors.Error {
                        if jiraIssue.ParentId != 0 {
                                issue.ParentIssueId = 
issueIdGen.Generate(data.Options.ConnectionId, jiraIssue.ParentId)
                        }
+                       result = append(result, issue)
                        boardIssue := &ticket.BoardIssue{
                                BoardId: boardId,
                                IssueId: issue.Id,
                        }
-                       return []interface{}{
-                               issue,
-                               boardIssue,
-                       }, nil
+                       result = append(result, boardIssue)
+                       return result, nil
                },
        })
        if err != nil {
diff --git a/backend/plugins/pagerduty/e2e/incident_test.go 
b/backend/plugins/pagerduty/e2e/incident_test.go
index 6076b8a0b..fd1536dfb 100644
--- a/backend/plugins/pagerduty/e2e/incident_test.go
+++ b/backend/plugins/pagerduty/e2e/incident_test.go
@@ -91,6 +91,7 @@ func TestIncidentDataFlow(t *testing.T) {
                },
        )
        dataflowTester.FlushTabler(&ticket.Issue{})
+       dataflowTester.FlushTabler(&ticket.IssueAssignee{})
        dataflowTester.Subtask(tasks.ConvertIncidentsMeta, taskData)
        dataflowTester.VerifyTableWithOptions(
                ticket.Issue{},
@@ -100,4 +101,8 @@ func TestIncidentDataFlow(t *testing.T) {
                        IgnoreFields: []string{"original_project"},
                },
        )
+       dataflowTester.VerifyTableWithOptions(ticket.IssueAssignee{}, 
e2ehelper.TableOptions{
+               CSVRelPath:  "./snapshot_tables/issue_assignees.csv",
+               IgnoreTypes: []interface{}{common.NoPKModel{}},
+       })
 }
diff --git a/backend/plugins/pagerduty/e2e/snapshot_tables/issue_assignees.csv 
b/backend/plugins/pagerduty/e2e/snapshot_tables/issue_assignees.csv
new file mode 100644
index 000000000..c2763d15c
--- /dev/null
+++ b/backend/plugins/pagerduty/e2e/snapshot_tables/issue_assignees.csv
@@ -0,0 +1,4 @@
+issue_id,assignee_id,assignee_name
+pagerduty:Incident:1:4,P25K520,Kian Amini
+pagerduty:Incident:1:4,PQYACO3,Keon Amini
+pagerduty:Incident:1:5,PQYACO3,Keon Amini
diff --git a/backend/plugins/pagerduty/tasks/incidents_converter.go 
b/backend/plugins/pagerduty/tasks/incidents_converter.go
index 7547bf5c5..e8448afd5 100644
--- a/backend/plugins/pagerduty/tasks/incidents_converter.go
+++ b/backend/plugins/pagerduty/tasks/incidents_converter.go
@@ -103,19 +103,25 @@ func ConvertIncidents(taskCtx plugin.SubTaskContext) 
errors.Error {
                                LeadTimeMinutes: leadTime,
                                Priority:        string(incident.Urgency),
                        }
+                       var result []interface{}
                        if combined.User != nil {
                                domainIssue.AssigneeId = combined.User.Id
                                domainIssue.AssigneeName = combined.User.Name
+                               issueAssignee := &ticket.IssueAssignee{
+                                       IssueId:      domainIssue.Id,
+                                       AssigneeId:   domainIssue.AssigneeId,
+                                       AssigneeName: domainIssue.AssigneeName,
+                               }
+                               result = append(result, issueAssignee)
                        }
+                       result = append(result, domainIssue)
                        seenIncidents[incident.Number] = combined
                        boardIssue := &ticket.BoardIssue{
                                BoardId: 
serviceIdGen.Generate(data.Options.ConnectionId, data.Options.ServiceId),
                                IssueId: domainIssue.Id,
                        }
-                       return []interface{}{
-                               boardIssue,
-                               domainIssue,
-                       }, nil
+                       result = append(result, boardIssue)
+                       return result, nil
                },
        })
        if err != nil {
diff --git a/backend/plugins/tapd/e2e/bugs_test.go 
b/backend/plugins/tapd/e2e/bugs_test.go
index 7bdbf4324..c88fcc026 100644
--- a/backend/plugins/tapd/e2e/bugs_test.go
+++ b/backend/plugins/tapd/e2e/bugs_test.go
@@ -100,6 +100,7 @@ func TestTapdBugDataFlow(t *testing.T) {
        dataflowTester.FlushTabler(&ticket.BoardIssue{})
        dataflowTester.FlushTabler(&ticket.SprintIssue{})
        dataflowTester.FlushTabler(&ticket.IssueLabel{})
+       dataflowTester.FlushTabler(&ticket.IssueAssignee{})
        dataflowTester.Subtask(tasks.ConvertBugMeta, taskData)
        dataflowTester.VerifyTable(
                ticket.Issue{},
@@ -149,6 +150,10 @@ func TestTapdBugDataFlow(t *testing.T) {
                        "sprint_id",
                ),
        )
+       dataflowTester.VerifyTableWithOptions(ticket.IssueAssignee{}, 
e2ehelper.TableOptions{
+               CSVRelPath:  "./snapshot_tables/bug_issue_assignees.csv",
+               IgnoreTypes: []interface{}{common.NoPKModel{}},
+       })
        dataflowTester.Subtask(tasks.ConvertBugLabelsMeta, taskData)
        dataflowTester.VerifyTable(
                ticket.IssueLabel{},
diff --git a/backend/plugins/tapd/e2e/snapshot_tables/bug_issue_assignees.csv 
b/backend/plugins/tapd/e2e/snapshot_tables/bug_issue_assignees.csv
new file mode 100644
index 000000000..642ab68dd
--- /dev/null
+++ b/backend/plugins/tapd/e2e/snapshot_tables/bug_issue_assignees.csv
@@ -0,0 +1,10 @@
+issue_id,assignee_id,assignee_name
+tapd:TapdBug:1:11991001001417,tapd:TapdAccount:1:test-11test-11test-11,test-11test-11test-11
+tapd:TapdBug:1:11991001001418,tapd:TapdAccount:1:test-11test-11test-11,test-11test-11test-11
+tapd:TapdBug:1:11991001001422,tapd:TapdAccount:1:test-11test-11test-11,test-11test-11test-11
+tapd:TapdBug:1:11991001001423,tapd:TapdAccount:1:test-11test-11test-11,test-11test-11test-11
+tapd:TapdBug:1:11991001001426,tapd:TapdAccount:1:test-11test-11test-11,test-11test-11test-11
+tapd:TapdBug:1:11991001001448,tapd:TapdAccount:1:test-11test-11test-11,test-11test-11test-11
+tapd:TapdBug:1:11991001001710,tapd:TapdAccount:1:test-11test-11test-11,test-11test-11test-11
+tapd:TapdBug:1:11991001001711,tapd:TapdAccount:1:test-11test-11test-11,test-11test-11test-11
+tapd:TapdBug:1:11991001001740,tapd:TapdAccount:1:郝骁宵,郝骁宵
diff --git a/backend/plugins/tapd/e2e/snapshot_tables/story_issue_assignees.csv 
b/backend/plugins/tapd/e2e/snapshot_tables/story_issue_assignees.csv
new file mode 100644
index 000000000..27a96ee61
--- /dev/null
+++ b/backend/plugins/tapd/e2e/snapshot_tables/story_issue_assignees.csv
@@ -0,0 +1,21 @@
+issue_id,assignee_id,assignee_name
+tapd:TapdStory:1:11991001037563,tapd:TapdAccount:1:test-11test-11test-11,test-11test-11test-11
+tapd:TapdStory:1:11991001037696,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdStory:1:11991001037697,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdStory:1:11991001038322,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdStory:1:11991001038323,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdStory:1:11991001038697,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdStory:1:11991001038911,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdStory:1:11991001038912,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdStory:1:11991001039664,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdStory:1:11991001039673,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdStory:1:11991001040086,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdStory:1:11991001040088,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdStory:1:11991001041163,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdStory:1:11991001041164,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdStory:1:11991001041165,tapd:TapdAccount:1:testUnicode9f50testUnicode9e9f,testUnicode9f50testUnicode9e9f
+tapd:TapdStory:1:11991001041166,tapd:TapdAccount:1:testUnicode9f50testUnicode9e9f,testUnicode9f50testUnicode9e9f
+tapd:TapdStory:1:11991001041788,tapd:TapdAccount:1:testUnicode6768testUnicode4e39,testUnicode6768testUnicode4e39
+tapd:TapdStory:1:11991001041789,tapd:TapdAccount:1:testUnicode6768testUnicode4e39,testUnicode6768testUnicode4e39
+tapd:TapdStory:1:11991001041899,tapd:TapdAccount:1:testUnicode5218testUnicode5b87testUnicode6615,testUnicode5218testUnicode5b87testUnicode6615
+tapd:TapdStory:1:11991001041900,tapd:TapdAccount:1:testUnicode5218testUnicode5b87testUnicode6615,testUnicode5218testUnicode5b87testUnicode6615
diff --git a/backend/plugins/tapd/e2e/snapshot_tables/task_issue_assignees.csv 
b/backend/plugins/tapd/e2e/snapshot_tables/task_issue_assignees.csv
new file mode 100644
index 000000000..f86a8ce5e
--- /dev/null
+++ b/backend/plugins/tapd/e2e/snapshot_tables/task_issue_assignees.csv
@@ -0,0 +1,16 @@
+issue_id,assignee_id,assignee_name
+tapd:TapdTask:1:11991001015107,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdTask:1:11991001015121,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdTask:1:11991001015142,tapd:TapdAccount:1:test-11test-11test-11,test-11test-11test-11
+tapd:TapdTask:1:11991001015184,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdTask:1:11991001015203,tapd:TapdAccount:1:test-11test-11test-11,test-11test-11test-11
+tapd:TapdTask:1:11991001015207,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdTask:1:11991001015253,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdTask:1:11991001015307,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdTask:1:11991001015309,tapd:TapdAccount:1:test-11test-11test-11,test-11test-11test-11
+tapd:TapdTask:1:11991001015340,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdTask:1:11991001015361,tapd:TapdAccount:1:test-11test-11test-11,test-11test-11test-11
+tapd:TapdTask:1:11991001015431,tapd:TapdAccount:1:test-11test-11test-11,test-11test-11test-11
+tapd:TapdTask:1:11991001015441,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdTask:1:11991001015452,tapd:TapdAccount:1:test-11test-11,test-11test-11
+tapd:TapdTask:1:11991001015583,tapd:TapdAccount:1:test-11test-11test-11,test-11test-11test-11
diff --git a/backend/plugins/tapd/e2e/stories_test.go 
b/backend/plugins/tapd/e2e/stories_test.go
index c72ad77c3..68957619b 100644
--- a/backend/plugins/tapd/e2e/stories_test.go
+++ b/backend/plugins/tapd/e2e/stories_test.go
@@ -101,6 +101,7 @@ func TestTapdStoryDataFlow(t *testing.T) {
        dataflowTester.FlushTabler(&ticket.BoardIssue{})
        dataflowTester.FlushTabler(&ticket.SprintIssue{})
        dataflowTester.FlushTabler(&ticket.IssueLabel{})
+       dataflowTester.FlushTabler(&ticket.IssueAssignee{})
        dataflowTester.Subtask(tasks.ConvertStoryMeta, taskData)
        dataflowTester.VerifyTableWithOptions(&ticket.Issue{}, 
e2ehelper.TableOptions{
                CSVRelPath:  "./snapshot_tables/issues_story.csv",
@@ -123,6 +124,11 @@ func TestTapdStoryDataFlow(t *testing.T) {
                        "sprint_id",
                ),
        )
+       dataflowTester.VerifyTableWithOptions(ticket.IssueAssignee{}, 
e2ehelper.TableOptions{
+               CSVRelPath:  "./snapshot_tables/story_issue_assignees.csv",
+               IgnoreTypes: []interface{}{common.NoPKModel{}},
+       })
+
        dataflowTester.Subtask(tasks.ConvertStoryLabelsMeta, taskData)
        dataflowTester.VerifyTable(
                ticket.IssueLabel{},
diff --git a/backend/plugins/tapd/e2e/tasks_test.go 
b/backend/plugins/tapd/e2e/tasks_test.go
index 0d9c8569c..0f23ddfda 100644
--- a/backend/plugins/tapd/e2e/tasks_test.go
+++ b/backend/plugins/tapd/e2e/tasks_test.go
@@ -93,6 +93,7 @@ func TestTapdTaskDataFlow(t *testing.T) {
        dataflowTester.FlushTabler(&ticket.BoardIssue{})
        dataflowTester.FlushTabler(&ticket.SprintIssue{})
        dataflowTester.FlushTabler(&ticket.IssueLabel{})
+       dataflowTester.FlushTabler(&ticket.IssueAssignee{})
        dataflowTester.Subtask(tasks.ConvertTaskMeta, taskData)
        dataflowTester.VerifyTableWithOptions(&ticket.Issue{}, 
e2ehelper.TableOptions{
                CSVRelPath:  "./snapshot_tables/issues_task.csv",
@@ -114,6 +115,11 @@ func TestTapdTaskDataFlow(t *testing.T) {
                        "sprint_id",
                ),
        )
+       dataflowTester.VerifyTableWithOptions(ticket.IssueAssignee{}, 
e2ehelper.TableOptions{
+               CSVRelPath:  "./snapshot_tables/task_issue_assignees.csv",
+               IgnoreTypes: []interface{}{common.NoPKModel{}},
+       })
+
        dataflowTester.Subtask(tasks.ConvertTaskLabelsMeta, taskData)
        dataflowTester.VerifyTable(
                ticket.IssueLabel{},
diff --git a/backend/plugins/tapd/tasks/bug_converter.go 
b/backend/plugins/tapd/tasks/bug_converter.go
index 7da20dbaa..aa32acb2d 100644
--- a/backend/plugins/tapd/tasks/bug_converter.go
+++ b/backend/plugins/tapd/tasks/bug_converter.go
@@ -76,13 +76,19 @@ func ConvertBug(taskCtx plugin.SubTaskContext) errors.Error 
{
                                Component:      toolL.Feature, // todo not sure 
about this
                                OriginalStatus: toolL.Status,
                        }
+                       var results []interface{}
                        if domainL.AssigneeName != "" {
                                domainL.AssigneeId = 
getAccountIdGen().Generate(data.Options.ConnectionId, toolL.CurrentOwner)
+                               issueAssignee := &ticket.IssueAssignee{
+                                       IssueId:      domainL.Id,
+                                       AssigneeId:   domainL.AssigneeId,
+                                       AssigneeName: domainL.AssigneeName,
+                               }
+                               results = append(results, issueAssignee)
                        }
                        if domainL.ResolutionDate != nil && domainL.CreatedDate 
!= nil {
                                domainL.LeadTimeMinutes = 
int64(domainL.ResolutionDate.Sub(*domainL.CreatedDate).Minutes())
                        }
-                       results := make([]interface{}, 0, 2)
                        boardIssue := &ticket.BoardIssue{
                                BoardId: 
getWorkspaceIdGen().Generate(toolL.ConnectionId, toolL.WorkspaceId),
                                IssueId: domainL.Id,
diff --git a/backend/plugins/tapd/tasks/story_converter.go 
b/backend/plugins/tapd/tasks/story_converter.go
index a4047c3c1..7fccbf781 100644
--- a/backend/plugins/tapd/tasks/story_converter.go
+++ b/backend/plugins/tapd/tasks/story_converter.go
@@ -79,13 +79,19 @@ func ConvertStory(taskCtx plugin.SubTaskContext) 
errors.Error {
                                Severity:             "",
                                Component:            toolL.Feature,
                        }
+                       var results []interface{}
                        if domainL.AssigneeName != "" {
                                domainL.AssigneeId = 
getAccountIdGen().Generate(data.Options.ConnectionId, toolL.Owner)
+                               issueAssignee := &ticket.IssueAssignee{
+                                       IssueId:      domainL.Id,
+                                       AssigneeId:   domainL.AssigneeId,
+                                       AssigneeName: domainL.AssigneeName,
+                               }
+                               results = append(results, issueAssignee)
                        }
                        if domainL.ResolutionDate != nil && domainL.CreatedDate 
!= nil {
                                domainL.LeadTimeMinutes = 
int64(domainL.ResolutionDate.Sub(*domainL.CreatedDate).Minutes())
                        }
-                       results := make([]interface{}, 0, 2)
                        boardIssue := &ticket.BoardIssue{
                                BoardId: 
getWorkspaceIdGen().Generate(toolL.ConnectionId, toolL.WorkspaceId),
                                IssueId: domainL.Id,
diff --git a/backend/plugins/tapd/tasks/task_converter.go 
b/backend/plugins/tapd/tasks/task_converter.go
index 2f71456ca..ee2ad9fb4 100644
--- a/backend/plugins/tapd/tasks/task_converter.go
+++ b/backend/plugins/tapd/tasks/task_converter.go
@@ -75,13 +75,19 @@ func ConvertTask(taskCtx plugin.SubTaskContext) 
errors.Error {
                                CreatorName:    toolL.Creator,
                                AssigneeName:   toolL.Owner,
                        }
+                       var results []interface{}
                        if domainL.AssigneeName != "" {
                                domainL.AssigneeId = 
getAccountIdGen().Generate(data.Options.ConnectionId, toolL.Owner)
+                               issueAssignee := &ticket.IssueAssignee{
+                                       IssueId:      domainL.Id,
+                                       AssigneeId:   domainL.AssigneeId,
+                                       AssigneeName: domainL.AssigneeName,
+                               }
+                               results = append(results, issueAssignee)
                        }
                        if domainL.ResolutionDate != nil && domainL.CreatedDate 
!= nil {
                                domainL.LeadTimeMinutes = 
int64(domainL.ResolutionDate.Sub(*domainL.CreatedDate).Minutes())
                        }
-                       results := make([]interface{}, 0, 2)
                        boardIssue := &ticket.BoardIssue{
                                BoardId: 
getWorkspaceIdGen().Generate(toolL.ConnectionId, toolL.WorkspaceId),
                                IssueId: domainL.Id,
diff --git a/backend/plugins/zentao/e2e/bug_test.go 
b/backend/plugins/zentao/e2e/bug_test.go
index 4057d29f5..36ed75785 100644
--- a/backend/plugins/zentao/e2e/bug_test.go
+++ b/backend/plugins/zentao/e2e/bug_test.go
@@ -18,13 +18,14 @@ limitations under the License.
 package e2e
 
 import (
+       "testing"
+
        "github.com/apache/incubator-devlake/core/models/common"
        "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
        "github.com/apache/incubator-devlake/helpers/e2ehelper"
        "github.com/apache/incubator-devlake/plugins/zentao/impl"
        "github.com/apache/incubator-devlake/plugins/zentao/models"
        "github.com/apache/incubator-devlake/plugins/zentao/tasks"
-       "testing"
 )
 
 func TestZentaoBugDataFlow(t *testing.T) {
@@ -55,6 +56,7 @@ func TestZentaoBugDataFlow(t *testing.T) {
        // verify conversion
        dataflowTester.FlushTabler(&ticket.Issue{})
        dataflowTester.FlushTabler(&ticket.BoardIssue{})
+       dataflowTester.FlushTabler(&ticket.IssueAssignee{})
        dataflowTester.Subtask(tasks.ConvertBugMeta, taskData)
        dataflowTester.VerifyTableWithOptions(&ticket.Issue{}, 
e2ehelper.TableOptions{
                CSVRelPath:   "./snapshot_tables/issues_bug.csv",
@@ -65,4 +67,8 @@ func TestZentaoBugDataFlow(t *testing.T) {
                CSVRelPath:  "./snapshot_tables/board_issues_bug.csv",
                IgnoreTypes: []interface{}{common.NoPKModel{}},
        })
+       dataflowTester.VerifyTableWithOptions(ticket.IssueAssignee{}, 
e2ehelper.TableOptions{
+               CSVRelPath:  "./snapshot_tables/bug_issue_assignees.csv",
+               IgnoreTypes: []interface{}{common.NoPKModel{}},
+       })
 }
diff --git a/backend/plugins/zentao/e2e/snapshot_tables/bug_issue_assignees.csv 
b/backend/plugins/zentao/e2e/snapshot_tables/bug_issue_assignees.csv
new file mode 100644
index 000000000..2f2e3ade7
--- /dev/null
+++ b/backend/plugins/zentao/e2e/snapshot_tables/bug_issue_assignees.csv
@@ -0,0 +1,5 @@
+issue_id,assignee_id,assignee_name
+zentao:ZentaoBug:1:1,4,开发甲
+zentao:ZentaoBug:1:2,0,
+zentao:ZentaoBug:1:3,4,开发甲
+zentao:ZentaoBug:1:4,9,测试丙
diff --git 
a/backend/plugins/zentao/e2e/snapshot_tables/story_issue_assignees.csv 
b/backend/plugins/zentao/e2e/snapshot_tables/story_issue_assignees.csv
new file mode 100644
index 000000000..ba8ab516a
--- /dev/null
+++ b/backend/plugins/zentao/e2e/snapshot_tables/story_issue_assignees.csv
@@ -0,0 +1,8 @@
+issue_id,assignee_id,assignee_name
+zentao:ZentaoStory:1:1,2,产品经理
+zentao:ZentaoStory:1:2,2,产品经理
+zentao:ZentaoStory:1:3,2,产品经理
+zentao:ZentaoStory:1:4,2,产品经理
+zentao:ZentaoStory:1:5,2,产品经理
+zentao:ZentaoStory:1:6,2,产品经理
+zentao:ZentaoStory:1:7,2,产品经理
diff --git 
a/backend/plugins/zentao/e2e/snapshot_tables/task_issue_assignees.csv 
b/backend/plugins/zentao/e2e/snapshot_tables/task_issue_assignees.csv
new file mode 100644
index 000000000..6b047e90b
--- /dev/null
+++ b/backend/plugins/zentao/e2e/snapshot_tables/task_issue_assignees.csv
@@ -0,0 +1,4 @@
+issue_id,assignee_id,assignee_name
+zentao:ZentaoTask:1:1,5,开发乙
+zentao:ZentaoTask:1:2,5,开发乙
+zentao:ZentaoTask:1:3,5,开发乙
diff --git a/backend/plugins/zentao/e2e/story_test.go 
b/backend/plugins/zentao/e2e/story_test.go
index 14ca04a4f..873caf8e1 100644
--- a/backend/plugins/zentao/e2e/story_test.go
+++ b/backend/plugins/zentao/e2e/story_test.go
@@ -18,13 +18,14 @@ limitations under the License.
 package e2e
 
 import (
+       "testing"
+
        "github.com/apache/incubator-devlake/core/models/common"
        "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
        "github.com/apache/incubator-devlake/helpers/e2ehelper"
        "github.com/apache/incubator-devlake/plugins/zentao/impl"
        "github.com/apache/incubator-devlake/plugins/zentao/models"
        "github.com/apache/incubator-devlake/plugins/zentao/tasks"
-       "testing"
 )
 
 func TestZentaoStoryDataFlow(t *testing.T) {
@@ -55,6 +56,7 @@ func TestZentaoStoryDataFlow(t *testing.T) {
        // verify conversion
        dataflowTester.FlushTabler(&ticket.Issue{})
        dataflowTester.FlushTabler(&ticket.BoardIssue{})
+       dataflowTester.FlushTabler(&ticket.IssueAssignee{})
        dataflowTester.Subtask(tasks.ConvertStoryMeta, taskData)
        dataflowTester.VerifyTableWithOptions(&ticket.Issue{}, 
e2ehelper.TableOptions{
                CSVRelPath:   "./snapshot_tables/issues_story.csv",
@@ -65,4 +67,8 @@ func TestZentaoStoryDataFlow(t *testing.T) {
                CSVRelPath:  "./snapshot_tables/board_issues_story.csv",
                IgnoreTypes: []interface{}{common.NoPKModel{}},
        })
+       dataflowTester.VerifyTableWithOptions(ticket.IssueAssignee{}, 
e2ehelper.TableOptions{
+               CSVRelPath:  "./snapshot_tables/story_issue_assignees.csv",
+               IgnoreTypes: []interface{}{common.NoPKModel{}},
+       })
 }
diff --git a/backend/plugins/zentao/e2e/task_test.go 
b/backend/plugins/zentao/e2e/task_test.go
index a7967570a..0cf711d1c 100644
--- a/backend/plugins/zentao/e2e/task_test.go
+++ b/backend/plugins/zentao/e2e/task_test.go
@@ -18,13 +18,14 @@ limitations under the License.
 package e2e
 
 import (
+       "testing"
+
        "github.com/apache/incubator-devlake/core/models/common"
        "github.com/apache/incubator-devlake/core/models/domainlayer/ticket"
        "github.com/apache/incubator-devlake/helpers/e2ehelper"
        "github.com/apache/incubator-devlake/plugins/zentao/impl"
        "github.com/apache/incubator-devlake/plugins/zentao/models"
        "github.com/apache/incubator-devlake/plugins/zentao/tasks"
-       "testing"
 )
 
 func TestZentaoTaskDataFlow(t *testing.T) {
@@ -54,6 +55,7 @@ func TestZentaoTaskDataFlow(t *testing.T) {
 
        dataflowTester.FlushTabler(&ticket.Issue{})
        dataflowTester.FlushTabler(&ticket.BoardIssue{})
+       dataflowTester.FlushTabler(&ticket.IssueAssignee{})
        dataflowTester.Subtask(tasks.ConvertTaskMeta, taskData)
        dataflowTester.VerifyTableWithOptions(&ticket.Issue{}, 
e2ehelper.TableOptions{
                CSVRelPath:   "./snapshot_tables/issues_task.csv",
@@ -64,4 +66,8 @@ func TestZentaoTaskDataFlow(t *testing.T) {
                CSVRelPath:  "./snapshot_tables/board_issues_task.csv",
                IgnoreTypes: []interface{}{common.NoPKModel{}},
        })
+       dataflowTester.VerifyTableWithOptions(ticket.IssueAssignee{}, 
e2ehelper.TableOptions{
+               CSVRelPath:  "./snapshot_tables/task_issue_assignees.csv",
+               IgnoreTypes: []interface{}{common.NoPKModel{}},
+       })
 }
diff --git a/backend/plugins/zentao/tasks/bug_convertor.go 
b/backend/plugins/zentao/tasks/bug_convertor.go
index 67f89a932..3ae03828b 100644
--- a/backend/plugins/zentao/tasks/bug_convertor.go
+++ b/backend/plugins/zentao/tasks/bug_convertor.go
@@ -18,6 +18,9 @@ limitations under the License.
 package tasks
 
 import (
+       "reflect"
+       "strconv"
+
        "github.com/apache/incubator-devlake/core/dal"
        "github.com/apache/incubator-devlake/core/errors"
        "github.com/apache/incubator-devlake/core/models/domainlayer"
@@ -26,8 +29,6 @@ import (
        "github.com/apache/incubator-devlake/core/plugin"
        "github.com/apache/incubator-devlake/helpers/pluginhelper/api"
        "github.com/apache/incubator-devlake/plugins/zentao/models"
-       "reflect"
-       "strconv"
 )
 
 var _ plugin.SubTaskEntryPoint = ConvertBug
@@ -98,11 +99,19 @@ func ConvertBug(taskCtx plugin.SubTaskContext) errors.Error 
{
                        if toolEntity.ClosedDate != nil {
                                domainEntity.LeadTimeMinutes = 
int64(toolEntity.ClosedDate.ToNullableTime().Sub(toolEntity.OpenedDate.ToTime()).Minutes())
                        }
+                       var results []interface{}
+                       if domainEntity.AssigneeId != "" {
+                               issueAssignee := &ticket.IssueAssignee{
+                                       IssueId:      domainEntity.Id,
+                                       AssigneeId:   domainEntity.AssigneeId,
+                                       AssigneeName: domainEntity.AssigneeName,
+                               }
+                               results = append(results, issueAssignee)
+                       }
                        domainBoardIssue := &ticket.BoardIssue{
                                BoardId: 
boardIdGen.Generate(data.Options.ConnectionId, data.Options.ProductId),
                                IssueId: domainEntity.Id,
                        }
-                       results := make([]interface{}, 0)
                        results = append(results, domainEntity, 
domainBoardIssue)
                        return results, nil
                },
diff --git a/backend/plugins/zentao/tasks/story_convertor.go 
b/backend/plugins/zentao/tasks/story_convertor.go
index f80093fbe..bfa9d3af5 100644
--- a/backend/plugins/zentao/tasks/story_convertor.go
+++ b/backend/plugins/zentao/tasks/story_convertor.go
@@ -96,6 +96,15 @@ func ConvertStory(taskCtx plugin.SubTaskContext) 
errors.Error {
                        default:
                                domainEntity.Status = ticket.IN_PROGRESS
                        }
+                       var results []interface{}
+                       if domainEntity.AssigneeId != "" {
+                               issueAssignee := &ticket.IssueAssignee{
+                                       IssueId:      domainEntity.Id,
+                                       AssigneeId:   domainEntity.AssigneeId,
+                                       AssigneeName: domainEntity.AssigneeName,
+                               }
+                               results = append(results, issueAssignee)
+                       }
                        if toolEntity.ClosedDate != nil {
                                domainEntity.LeadTimeMinutes = 
int64(toolEntity.ClosedDate.ToNullableTime().Sub(toolEntity.OpenedDate.ToTime()).Minutes())
                        }
@@ -103,7 +112,6 @@ func ConvertStory(taskCtx plugin.SubTaskContext) 
errors.Error {
                                BoardId: 
boardIdGen.Generate(data.Options.ConnectionId, data.Options.ProductId),
                                IssueId: domainEntity.Id,
                        }
-                       results := make([]interface{}, 0)
                        results = append(results, domainEntity, 
domainBoardIssue)
                        return results, nil
                },
diff --git a/backend/plugins/zentao/tasks/task_convertor.go 
b/backend/plugins/zentao/tasks/task_convertor.go
index c28271187..22f44a9f4 100644
--- a/backend/plugins/zentao/tasks/task_convertor.go
+++ b/backend/plugins/zentao/tasks/task_convertor.go
@@ -100,11 +100,19 @@ func ConvertTask(taskCtx plugin.SubTaskContext) 
errors.Error {
                        if toolEntity.ClosedDate != nil {
                                domainEntity.LeadTimeMinutes = 
int64(toolEntity.ClosedDate.ToNullableTime().Sub(toolEntity.OpenedDate.ToTime()).Minutes())
                        }
+                       var results []interface{}
+                       if domainEntity.AssigneeId != "" {
+                               issueAssignee := &ticket.IssueAssignee{
+                                       IssueId:      domainEntity.Id,
+                                       AssigneeId:   domainEntity.AssigneeId,
+                                       AssigneeName: domainEntity.AssigneeName,
+                               }
+                               results = append(results, issueAssignee)
+                       }
                        domainBoardIssue := &ticket.BoardIssue{
                                BoardId: 
boardIdGen.Generate(data.Options.ConnectionId, toolEntity.Execution),
                                IssueId: domainEntity.Id,
                        }
-                       results := make([]interface{}, 0)
                        results = append(results, domainEntity, 
domainBoardIssue)
                        return results, nil
                },

Reply via email to