Skip to content

Commit 8160c1a

Browse files
committed
feat: #70 continue when break
1 parent 19f0d83 commit 8160c1a

File tree

2 files changed

+80
-11
lines changed

2 files changed

+80
-11
lines changed

app/Commands/WikiImportCommand.php

+18-10
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use DOMDocument;
99
use Exception;
1010
use Illuminate\Contracts\Filesystem\FileNotFoundException;
11+
use Illuminate\Support\Facades\File;
1112
use Illuminate\Support\Str;
1213
use LaravelFans\Confluence\Facades\Confluence;
1314
use LaravelZero\Framework\Commands\Command;
@@ -20,6 +21,7 @@ class WikiImportCommand extends Command
2021
protected Disk $codingDisk;
2122
protected Wiki $codingWiki;
2223
protected array $errors = [];
24+
protected array $importedPages = [];
2325

2426
/**
2527
* The signature of the command.
@@ -37,6 +39,7 @@ class WikiImportCommand extends Command
3739
{--coding_team_domain= : CODING 团队域名,如 xxx.coding.net 即填写 xxx}
3840
{--coding_project_uri= : CODING 项目标识,如 xxx.coding.net/p/yyy 即填写 yyy}
3941
{--save-markdown : 本地保留生成的 Markdown 文件}
42+
{--continue : 断点续传}
4043
';
4144

4245
/**
@@ -194,6 +197,9 @@ private function handleConfluenceHtml(): int
194197

195198
private function uploadConfluencePages(string $htmlDir, array $tree, array $titles, int $parentId = 0): void
196199
{
200+
if ($this->option('continue') && file_exists($htmlDir . DIRECTORY_SEPARATOR . 'success.log')) {
201+
$this->importedPages = parse_ini_file($htmlDir . DIRECTORY_SEPARATOR . 'success.log');
202+
}
197203
foreach ($tree as $page => $subPages) {
198204
$title = $titles[$page];
199205
$wikiId = $this->uploadConfluencePage($htmlDir . DIRECTORY_SEPARATOR . $page, $title, $parentId);
@@ -207,6 +213,11 @@ private function uploadConfluencePages(string $htmlDir, array $tree, array $titl
207213

208214
private function uploadConfluencePage(string $filePath, string $title = '', int $parentId = 0): int
209215
{
216+
$page = basename($filePath);
217+
if ($this->option('continue') && isset($this->importedPages[$page])) {
218+
$this->warn('断点续传,跳过页面:' . $page);
219+
return $this->importedPages[$page];
220+
}
210221
try {
211222
$markdown = $this->confluence->htmlFile2Markdown($filePath);
212223
} catch (FileNotFoundException $e) {
@@ -223,7 +234,6 @@ private function uploadConfluencePage(string $filePath, string $title = '', int
223234
$this->info('标题:' . $title);
224235

225236
$htmlDir = dirname($filePath);
226-
$page = basename($filePath);
227237
$markdown = $this->dealAttachments($filePath, $markdown);
228238
$mdFilename = substr($page, 0, -5) . '.md';
229239
if ($this->option('save-markdown')) {
@@ -244,22 +254,20 @@ private function uploadConfluencePage(string $filePath, string $title = '', int
244254
$this->codingProjectUri,
245255
$result['JobId']
246256
);
247-
} catch (Exception $e) {
248-
$message = '错误:导入失败,跳过 ' . $title . ' ' . $page;
249-
$this->error($message);
250-
$this->errors[] = $message;
251-
return false;
252-
}
253-
if ($jobStatus['Status'] == 'success') {
257+
if ($jobStatus['Status'] != 'success') {
258+
throw new Exception('job status ' . $jobStatus['Status']);
259+
}
254260
$wikiId = intval($jobStatus['Iids'][0]);
255-
}
256-
if (empty($wikiId)) {
261+
} catch (Exception $e) {
257262
$message = '错误:导入失败,跳过 ' . $title . ' ' . $page;
258263
$this->error($message);
259264
$this->errors[] = $message;
260265
return false;
261266
}
262267
$this->codingWiki->updateTitle($this->codingToken, $this->codingProjectUri, $wikiId, $title);
268+
if ($this->option('continue')) {
269+
file_put_contents($htmlDir . DIRECTORY_SEPARATOR . 'success.log', "$page = $wikiId\n", FILE_APPEND);
270+
}
263271
return $wikiId;
264272
}
265273

tests/Feature/WikiImportCommandTest.php

+62-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use App\Coding\Disk;
66
use App\Coding\Wiki;
77
use Confluence\Content;
8+
use Illuminate\Support\Facades\File;
89
use LaravelFans\Confluence\Facades\Confluence;
910
use Mockery\MockInterface;
1011
use Tests\TestCase;
@@ -180,7 +181,8 @@ public function testHandleConfluenceHtmlSuccess()
180181
$this->instance(Disk::class, $mockDisk);
181182
$mockDisk->shouldReceive('uploadAttachments')->times(4)->andReturn([]);
182183

183-
$this->artisan('wiki:import', ['--save-markdown' => true])
184+
File::delete($this->dataDir . '/confluence/space1/success.log');
185+
$this->artisan('wiki:import', ['--save-markdown' => true, '--continue' => true])
184186
->expectsQuestion('数据来源?', 'Confluence')
185187
->expectsQuestion('数据类型?', 'HTML')
186188
->expectsQuestion('空间导出的 HTML zip 文件路径', $this->dataDir . 'confluence/space1/')
@@ -207,6 +209,11 @@ public function testHandleConfluenceHtmlSuccess()
207209
unlink($this->dataDir . '/confluence/space1/65591.md');
208210
unlink($this->dataDir . '/confluence/space1/attachment-demo_65615.md');
209211
unlink($this->dataDir . '/confluence/space1/text-demo_65601.md');
212+
$log = "image-demo_65619.html = 27\n"
213+
. "65591.html = 27\n"
214+
. "attachment-demo_65615.html = 27\n"
215+
. "text-demo_65601.html = 27\n";
216+
$this->assertEquals($log, file_get_contents($this->dataDir . '/confluence/space1/success.log'));
210217
}
211218

212219
public function testAskNothing()
@@ -299,4 +306,58 @@ public function testHandleConfluenceSingleHtmlSuccess()
299306
->expectsOutput('上传成功,正在处理,任务 ID:a12353fa-f45b-4af2-83db-666bf9f66615')
300307
->assertExitCode(0);
301308
}
309+
310+
public function testHandleConfluenceHtmlContinueSuccess()
311+
{
312+
$this->setConfig();
313+
314+
// 注意:不能使用 partialMock
315+
// https://laracasts.com/discuss/channels/testing/this-partialmock-doesnt-call-the-constructor
316+
$mock = \Mockery::mock(Wiki::class, [])->makePartial();
317+
$this->instance(Wiki::class, $mock);
318+
319+
$mock->shouldReceive('createWikiByUploadZip')->times(2)->andReturn(json_decode(
320+
file_get_contents($this->dataDir . 'coding/' . 'CreateWikiByZipResponse.json'),
321+
true
322+
)['Response']);
323+
$mock->shouldReceive('getImportJobStatus')->times(2)->andReturn(json_decode(
324+
file_get_contents($this->dataDir . 'coding/' . 'DescribeImportJobStatusResponse.json'),
325+
true
326+
)['Response']['Data']);
327+
$mock->shouldReceive('updateTitle')->times(2)->andReturn(true);
328+
329+
330+
$mockDisk = \Mockery::mock(Disk::class, [])->makePartial();
331+
$this->instance(Disk::class, $mockDisk);
332+
$mockDisk->shouldReceive('uploadAttachments')->times(2)->andReturn([]);
333+
334+
$log = "image-demo_65619.html = 27\n"
335+
. "65591.html = 27\n";
336+
file_put_contents($this->dataDir . '/confluence/space1/success.log', $log);
337+
$this->artisan('wiki:import', ['--continue' => true])
338+
->expectsQuestion('数据来源?', 'Confluence')
339+
->expectsQuestion('数据类型?', 'HTML')
340+
->expectsQuestion('空间导出的 HTML zip 文件路径', $this->dataDir . 'confluence/space1/')
341+
->expectsOutput('空间名称:空间 1')
342+
->expectsOutput('空间标识:space1')
343+
->expectsOutput('发现 3 个一级页面')
344+
->expectsOutput("开始导入 CODING:")
345+
->expectsOutput('页面不存在:' . $this->dataDir . 'confluence/space1/not-found.html')
346+
->expectsOutput('断点续传,跳过页面:image-demo_65619.html')
347+
->expectsOutput('断点续传,跳过页面:65591.html')
348+
->expectsOutput('发现 2 个子页面')
349+
->expectsOutput('标题:Attachment Demo')
350+
->expectsOutput('上传成功,正在处理,任务 ID:a12353fa-f45b-4af2-83db-666bf9f66615')
351+
->expectsOutput('标题:Text Demo')
352+
->expectsOutput('上传成功,正在处理,任务 ID:a12353fa-f45b-4af2-83db-666bf9f66615')
353+
->expectsOutput('报错信息汇总:')
354+
->expectsOutput('页面不存在:' . $this->dataDir . 'confluence/space1/not-found.html')
355+
->assertExitCode(1);
356+
$log = "image-demo_65619.html = 27\n"
357+
. "65591.html = 27\n"
358+
. "attachment-demo_65615.html = 27\n"
359+
. "text-demo_65601.html = 27\n";
360+
$this->assertEquals($log, file_get_contents($this->dataDir . '/confluence/space1/success.log'));
361+
unlink($this->dataDir . '/confluence/space1/success.log');
362+
}
302363
}

0 commit comments

Comments
 (0)