Skip to content

Commit 39fc3aa

Browse files
committed
feat: #72 issue parent code
1 parent 4c8f0d6 commit 39fc3aa

File tree

4 files changed

+136
-51
lines changed

4 files changed

+136
-51
lines changed

Diff for: app/Commands/IssueImportCommand.php

+14-4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class IssueImportCommand extends Command
3434

3535
protected array $iterationMap = [];
3636
protected array $issueTypes = [];
37+
protected array $issueCodeMap = [];
3738

3839
/**
3940
* Execute the console command.
@@ -50,21 +51,25 @@ public function handle(Issue $codingIssue, Project $codingProject, Iteration $it
5051
}
5152

5253
$rows = FastExcel::import($filePath);
54+
if (!empty($rows) && isset($rows[0]['ID'])) {
55+
$rows = $rows->sortBy('ID');
56+
}
5357
foreach ($rows as $row) {
5458
try {
55-
$iterationResult = $this->createByRow($codingProject, $codingIssue, $iteration, $row);
59+
$issueResult = $this->createIssueByRow($codingProject, $codingIssue, $iteration, $row);
5660
} catch (\Exception $e) {
5761
$this->error('Error: ' . $e->getMessage());
5862
return 1;
5963
}
64+
$this->info('标题:' . $row['标题']);
6065
$this->info("https://{$this->codingTeamDomain}.coding.net/p/{$this->codingProjectUri}" .
61-
"/all/issues/${iterationResult['Code']}");
66+
"/all/issues/${issueResult['Code']}");
6267
}
6368

6469
return 0;
6570
}
6671

67-
private function createByRow(Project $codingProject, Issue $issue, Iteration $iteration, array $row)
72+
private function createIssueByRow(Project $codingProject, Issue $issue, Iteration $iteration, array $row)
6873
{
6974
if (empty($this->issueTypes)) {
7075
$result = $codingProject->getIssueTypes($this->codingToken, $this->codingProjectUri);
@@ -78,8 +83,13 @@ private function createByRow(Project $codingProject, Issue $issue, Iteration $it
7883
'Name' => $row['标题'],
7984
'Priority' => \App\Models\Issue::PRIORITY_MAP[$row['优先级']],
8085
'IterationCode' => $row['所属迭代'] ? $this->getIterationCode($iteration, $row['所属迭代']) : null,
86+
'ParentCode' => !empty($row['ParentCode']) ? $this->issueCodeMap[$row['ParentCode']] : null,
8187
];
82-
return $issue->create($this->codingToken, $this->codingProjectUri, $data);
88+
$result = $issue->create($this->codingToken, $this->codingProjectUri, $data);
89+
if (isset($row['ID'])) {
90+
$this->issueCodeMap[$row['ID']] = intval($result['Code']);
91+
}
92+
return $result;
8393
}
8494

8595
private function getIterationCode(Iteration $iteration, string $name)

Diff for: tests/Feature/IssueImportCommandTest.php

+88-17
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,19 @@
99

1010
class IssueImportCommandTest extends TestCase
1111
{
12-
private string $codingToken;
13-
private string $codingTeamDomain;
14-
private string $codingProjectUri;
12+
private string $token;
13+
private string $teamDomain;
14+
private string $projectUri;
1515

1616
protected function setUp(): void
1717
{
1818
parent::setUp();
19-
$this->codingToken = $this->faker->md5;
20-
config(['coding.token' => $this->codingToken]);
21-
$this->codingTeamDomain = $this->faker->domainWord;
22-
config(['coding.team_domain' => $this->codingTeamDomain]);
23-
$this->codingProjectUri = $this->faker->slug;
24-
config(['coding.project_uri' => $this->codingProjectUri]);
19+
$this->token = $this->faker->md5;
20+
config(['coding.token' => $this->token]);
21+
$this->teamDomain = $this->faker->domainWord;
22+
config(['coding.team_domain' => $this->teamDomain]);
23+
$this->projectUri = $this->faker->slug;
24+
config(['coding.project_uri' => $this->projectUri]);
2525
}
2626

2727
public function testImportSuccess()
@@ -56,11 +56,11 @@ public function testImportSuccess()
5656
$issueMock->shouldReceive('create')->times(21)->andReturn(...$results);
5757

5858
$this->artisan('issue:import', ['file' => $this->dataDir . 'coding/scrum-issues.csv'])
59-
->expectsOutput("https://$this->codingTeamDomain.coding.net/p/$this->codingProjectUri/all/issues/1")
60-
->expectsOutput("https://$this->codingTeamDomain.coding.net/p/$this->codingProjectUri/all/issues/2")
61-
->expectsOutput("https://$this->codingTeamDomain.coding.net/p/$this->codingProjectUri/all/issues/3")
62-
->expectsOutput("https://$this->codingTeamDomain.coding.net/p/$this->codingProjectUri/all/issues/20")
63-
->expectsOutput("https://$this->codingTeamDomain.coding.net/p/$this->codingProjectUri/all/issues/21")
59+
->expectsOutput("https://$this->teamDomain.coding.net/p/$this->projectUri/all/issues/1")
60+
->expectsOutput("https://$this->teamDomain.coding.net/p/$this->projectUri/all/issues/2")
61+
->expectsOutput("https://$this->teamDomain.coding.net/p/$this->projectUri/all/issues/3")
62+
->expectsOutput("https://$this->teamDomain.coding.net/p/$this->projectUri/all/issues/20")
63+
->expectsOutput("https://$this->teamDomain.coding.net/p/$this->projectUri/all/issues/21")
6464
->assertExitCode(0);
6565
}
6666

@@ -91,20 +91,91 @@ public function testImportUserStorySuccess()
9191
)['Response']['Iteration']);
9292

9393
$issueMock->shouldReceive('create')->times(1)->withArgs([
94-
$this->codingToken,
95-
$this->codingProjectUri,
94+
$this->token,
95+
$this->projectUri,
9696
[
9797
'Type' => 'REQUIREMENT',
9898
'IssueTypeId' => 213218,
9999
'Name' => '用户可通过手机号注册账户',
100100
'Priority' => "1",
101101
'IterationCode' => 2746,
102+
'ParentCode' => null,
102103
]
103104
])->andReturn($result);
104105

105106
$this->artisan('issue:import', ['file' => $this->dataDir . 'coding/scrum-issue-5.csv'])
106-
->expectsOutput("https://$this->codingTeamDomain.coding.net/p/$this->codingProjectUri/all/issues/" .
107+
->expectsOutput('标题:用户可通过手机号注册账户')
108+
->expectsOutput("https://$this->teamDomain.coding.net/p/$this->projectUri/all/issues/" .
107109
$result['Code'])
108110
->assertExitCode(0);
109111
}
112+
113+
public function testImportSubTask()
114+
{
115+
$mock = \Mockery::mock(Project::class, [])->makePartial();
116+
$this->instance(Project::class, $mock);
117+
118+
$mock->shouldReceive('getIssueTypes')->times(1)->andReturn(json_decode(
119+
file_get_contents($this->dataDir . 'coding/' . 'DescribeProjectIssueTypeListResponse.json'),
120+
true
121+
)['Response']['IssueTypes']);
122+
123+
$issueMock = \Mockery::mock(Issue::class, [])->makePartial();
124+
$this->instance(Issue::class, $issueMock);
125+
126+
$response = json_decode(
127+
file_get_contents($this->dataDir . 'coding/' . 'CreateIssueResponse.json'),
128+
true
129+
)['Response']['Issue'];
130+
131+
$parentIssue = $response;
132+
$issueMock->shouldReceive('create')->times(1)->withArgs([
133+
$this->token,
134+
$this->projectUri,
135+
[
136+
'Type' => 'REQUIREMENT',
137+
'IssueTypeId' => 213218,
138+
'Name' => '用户可通过手机号注册账户',
139+
'Priority' => "1",
140+
'IterationCode' => null,
141+
'ParentCode' => null,
142+
]
143+
])->andReturn($parentIssue);
144+
145+
$subTask1 = $response;
146+
$subTask1['Code'] = $this->faker->randomNumber();
147+
$issueMock->shouldReceive('create')->times(1)->withArgs([
148+
$this->token,
149+
$this->projectUri,
150+
[
151+
'Type' => 'SUB_TASK',
152+
'IssueTypeId' => 213222,
153+
'Name' => '完成手机号注册的短信验证码发送接口',
154+
'Priority' => "1",
155+
'IterationCode' => null,
156+
'ParentCode' => 2742,
157+
]
158+
])->andReturn($subTask1);
159+
160+
$subTask2 = $response;
161+
$subTask2['Code'] = $this->faker->randomNumber();
162+
$issueMock->shouldReceive('create')->times(1)->withArgs([
163+
$this->token,
164+
$this->projectUri,
165+
[
166+
'Type' => 'SUB_TASK',
167+
'IssueTypeId' => 213222,
168+
'Name' => '完成通过手机号注册用户的接口',
169+
'Priority' => "1",
170+
'IterationCode' => null,
171+
'ParentCode' => 2742,
172+
]
173+
])->andReturn($subTask2);
174+
175+
$this->artisan('issue:import', ['file' => $this->dataDir . 'coding/scrum-issues-5-6-7.csv'])
176+
->expectsOutput("https://$this->teamDomain.coding.net/p/$this->projectUri/all/issues/2742")
177+
->expectsOutput("https://$this->teamDomain.coding.net/p/$this->projectUri/all/issues/" . $subTask1['Code'])
178+
->expectsOutput("https://$this->teamDomain.coding.net/p/$this->projectUri/all/issues/" . $subTask2['Code'])
179+
->assertExitCode(0);
180+
}
110181
}

Diff for: tests/data/coding/scrum-issues-5-6-7.csv

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
ID,ParentCode,事项类型,标题,描述,状态,创建时间,创建人,更新时间,所属迭代,故事点,处理人,缺陷类型,优先级,截止日期,模块,标签,关注人,开始日期
2+
7,5,子工作项,完成通过手机号注册用户的接口,,已完成,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,,,,,中,,,,,
3+
6,5,子工作项,完成手机号注册的短信验证码发送接口,,已完成,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,,,,,中,,,,,
4+
5,,用户故事,用户可通过手机号注册账户,,开发中,2021-10-19 11:26:37,sinkcup,2021-10-19 11:26:37,,2,sinkcup,,中,2021-10-21,,,,

Diff for: tests/data/coding/scrum-issues.csv

+30-30
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
1-
ID,事项类型,标题,描述,状态,创建时间,创建人,更新时间,所属迭代,故事点,处理人,缺陷类型,优先级,截止日期,模块,标签,关注人,开始日期
2-
23,缺陷,商品详情页中商品价格字体应当显示为红色并且加粗,"步骤:
3-
4-
测试环境中,打开商品列表页;
5-
点击任意商品进详情页。
6-
测试结果:商品的价格字体显示为正常大小,颜色为黑色。
1+
ID,ParentCode,事项类型,标题,描述,状态,创建时间,创建人,更新时间,所属迭代,故事点,处理人,缺陷类型,优先级,截止日期,模块,标签,关注人,开始日期
2+
23,,缺陷,商品详情页中商品价格字体应当显示为红色并且加粗,"步骤:
3+
4+
测试环境中,打开商品列表页;
5+
点击任意商品进详情页。
6+
测试结果:商品的价格字体显示为正常大小,颜色为黑色。
77
预期结果:商品价格字体为红色加粗。",待处理,2021-10-19 11:26:39,sinkcup,2021-10-19 11:26:39,,,,,中,,,,,
8-
22,缺陷,登录页输入正确的用户名和密码后提示“用户不存在”,"步骤:
9-
测试环境中,输入URL https://mywebsite.com/login 进入登录页;
10-
输入用户名 Admin 和密码 mypassword;
11-
点击“登录”按钮。
12-
测试结果:页面提示“用户不存在”。
8+
22,,缺陷,登录页输入正确的用户名和密码后提示“用户不存在”,"步骤:
9+
测试环境中,输入URL https://mywebsite.com/login 进入登录页;
10+
输入用户名 Admin 和密码 mypassword;
11+
点击“登录”按钮。
12+
测试结果:页面提示“用户不存在”。
1313
预期结果:提示“登录成功”并且页面自动跳转到首页。",处理中,2021-10-19 11:26:39,sinkcup,2021-10-19 11:26:39,,,sinkcup,,中,,,,,
14-
21,任务,编制新功能的帮助文档并发布,,未开始,2021-10-19 11:26:39,sinkcup,2021-10-19 11:26:39,,,,,中,,,,,
15-
20,任务,编写脚本将 Excel 中的线下订单转换为商城后台订单,,未开始,2021-10-19 11:26:39,sinkcup,2021-10-19 11:26:39,,,,,中,,,,,
16-
19,任务,注册腾讯云账户,搭建测试环境和生产环境服务器,,处理中,2021-10-19 11:26:39,sinkcup,2021-10-19 11:26:39,第 1 次迭代,3,sinkcup,,中,2021-10-21,,,,
17-
18,用户故事,用户可对未支付的订单执行取消订单操作,,未开始,2021-10-19 11:26:39,sinkcup,2021-10-19 11:26:39,,,,,中,,,,,
18-
17,用户故事,管理员可在商城后台对订单执行发货操作,,未开始,2021-10-19 11:26:39,sinkcup,2021-10-19 11:26:39,,3,,,中,,,,,
19-
16,用户故事,用户可在手机端搜索并查看指定的订单详情,,未开始,2021-10-19 11:26:39,sinkcup,2021-10-19 11:26:39,,1,,,中,,,,,
20-
15,用户故事,通过访问邀请链接可注册成为商城用户,,未开始,2021-10-19 11:26:39,sinkcup,2021-10-19 11:26:39,,,,,中,,,,,
21-
14,用户故事,管理员可取消未发货且状态异常的订单,,未开始,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,第 2 次迭代,3,,,中,,,,,
22-
13,用户故事,用户可在“个人信息”中编辑个人基本信息,包括修改密码,,未开始,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,第 2 次迭代,2,sinkcup,,中,,,,,
23-
12,用户故事,管理员可在商城后台搜索订单,,未开始,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,第 1 次迭代,2,,,中,,,,,
24-
11,用户故事,管理员可在商城后台手工为用户下单,,未开始,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,第 1 次迭代,5,,,中,2021-10-26,,,,
25-
10,用户故事,用户可在个人中心的“个人信息”中查看个人信息,,已完成,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,第 1 次迭代,1,,,中,,,,,
26-
9,用户故事,用户可通过短信验证码登录商城,,开发中,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,第 1 次迭代,,,,中,,,,,
27-
8,子工作项,完成用户注册页面控件并集成后端接口,,处理中,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,第 1 次迭代,,,,中,2021-10-21,,,,
28-
7,子工作项,完成通过手机号注册用户的接口,,已完成,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,第 1 次迭代,,,,中,,,,,
29-
6,子工作项,完成手机号注册的短信验证码发送接口,,已完成,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,第 1 次迭代,,,,中,,,,,
30-
5,用户故事,用户可通过手机号注册账户,,开发中,2021-10-19 11:26:37,sinkcup,2021-10-19 11:26:37,第 1 次迭代,2,sinkcup,,中,2021-10-21,,,,
31-
2,史诗,订单管理,订单管理将实现用户的订单列表查询、订单详情、订单改价、订单地址修改、申请售后、订单取消等功能,未开始,2021-10-19 11:26:37,sinkcup,2021-10-19 11:26:37,,,,,中,,,,,2021-10-19
32-
1,史诗,用户管理,用户管理将实现用户的注册、邀请、用户查询、个人信息管理、删除用户、注销账户等功能。,未开始,2021-10-19 11:26:37,sinkcup,2021-10-19 11:26:37,,,,,中,,,,,2021-10-19
14+
21,,任务,编制新功能的帮助文档并发布,,未开始,2021-10-19 11:26:39,sinkcup,2021-10-19 11:26:39,,,,,中,,,,,
15+
20,,任务,编写脚本将 Excel 中的线下订单转换为商城后台订单,,未开始,2021-10-19 11:26:39,sinkcup,2021-10-19 11:26:39,,,,,中,,,,,
16+
19,,任务,注册腾讯云账户,搭建测试环境和生产环境服务器,,处理中,2021-10-19 11:26:39,sinkcup,2021-10-19 11:26:39,第 1 次迭代,3,sinkcup,,中,2021-10-21,,,,
17+
18,,用户故事,用户可对未支付的订单执行取消订单操作,,未开始,2021-10-19 11:26:39,sinkcup,2021-10-19 11:26:39,,,,,中,,,,,
18+
17,,用户故事,管理员可在商城后台对订单执行发货操作,,未开始,2021-10-19 11:26:39,sinkcup,2021-10-19 11:26:39,,3,,,中,,,,,
19+
16,,用户故事,用户可在手机端搜索并查看指定的订单详情,,未开始,2021-10-19 11:26:39,sinkcup,2021-10-19 11:26:39,,1,,,中,,,,,
20+
15,,用户故事,通过访问邀请链接可注册成为商城用户,,未开始,2021-10-19 11:26:39,sinkcup,2021-10-19 11:26:39,,,,,中,,,,,
21+
14,,用户故事,管理员可取消未发货且状态异常的订单,,未开始,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,第 2 次迭代,3,,,中,,,,,
22+
13,,用户故事,用户可在“个人信息”中编辑个人基本信息,包括修改密码,,未开始,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,第 2 次迭代,2,sinkcup,,中,,,,,
23+
12,,用户故事,管理员可在商城后台搜索订单,,未开始,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,第 1 次迭代,2,,,中,,,,,
24+
11,,用户故事,管理员可在商城后台手工为用户下单,,未开始,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,第 1 次迭代,5,,,中,2021-10-26,,,,
25+
10,,用户故事,用户可在个人中心的“个人信息”中查看个人信息,,已完成,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,第 1 次迭代,1,,,中,,,,,
26+
9,,用户故事,用户可通过短信验证码登录商城,,开发中,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,第 1 次迭代,,,,中,,,,,
27+
8,5,子工作项,完成用户注册页面控件并集成后端接口,,处理中,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,第 1 次迭代,,,,中,2021-10-21,,,,
28+
7,5,子工作项,完成通过手机号注册用户的接口,,已完成,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,第 1 次迭代,,,,中,,,,,
29+
6,5,子工作项,完成手机号注册的短信验证码发送接口,,已完成,2021-10-19 11:26:38,sinkcup,2021-10-19 11:26:38,第 1 次迭代,,,,中,,,,,
30+
5,,用户故事,用户可通过手机号注册账户,,开发中,2021-10-19 11:26:37,sinkcup,2021-10-19 11:26:37,第 1 次迭代,2,sinkcup,,中,2021-10-21,,,,
31+
2,,史诗,订单管理,订单管理将实现用户的订单列表查询、订单详情、订单改价、订单地址修改、申请售后、订单取消等功能,未开始,2021-10-19 11:26:37,sinkcup,2021-10-19 11:26:37,,,,,中,,,,,2021-10-19
32+
1,,史诗,用户管理,用户管理将实现用户的注册、邀请、用户查询、个人信息管理、删除用户、注销账户等功能。,未开始,2021-10-19 11:26:37,sinkcup,2021-10-19 11:26:37,,,,,中,,,,,2021-10-19

0 commit comments

Comments
 (0)