feat: 增强文档转换失败处理机制

- 新增 FixStuckDocuments 命令用于修复卡住的文档
- 支持批量检测和修复超时的转换任务
- 改进重试按钮,支持 failed/processing/pending 状态
- 在确认对话框中显示当前状态和错误信息
- 提供 dry-run 模式预览修复操作
This commit is contained in:
2026-03-12 15:58:24 +08:00
parent ed9260d5a6
commit 29c209116e
2 changed files with 113 additions and 2 deletions

View File

@@ -0,0 +1,103 @@
<?php
namespace App\Console\Commands;
use App\Models\Document;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class FixStuckDocuments extends Command
{
/**
* 命令签名
*
* @var string
*/
protected $signature = 'documents:fix-stuck
{--timeout=30 : 超过多少分钟未完成视为卡住默认30分钟}
{--status=processing : 要修复的状态processing/pending}
{--dry-run : 仅显示将要修复的文档,不实际执行}';
/**
* 命令描述
*
* @var string
*/
protected $description = '修复卡在转换中但实际已失败的文档';
/**
* 执行命令
*/
public function handle(): int
{
$timeout = (int) $this->option('timeout');
$status = $this->option('status');
$dryRun = $this->option('dry-run');
$this->info("正在查找卡住的文档...");
$this->info("状态: {$status}");
$this->info("超时时间: {$timeout} 分钟");
// 查找卡住的文档
$stuckDocuments = Document::where('conversion_status', $status)
->where('updated_at', '<', now()->subMinutes($timeout))
->get();
if ($stuckDocuments->isEmpty()) {
$this->info('✓ 没有发现卡住的文档');
return self::SUCCESS;
}
$this->warn("发现 {$stuckDocuments->count()} 个卡住的文档:");
// 显示文档列表
$tableData = $stuckDocuments->map(function ($doc) {
return [
'ID' => $doc->id,
'标题' => \Illuminate\Support\Str::limit($doc->title, 40),
'状态' => $doc->conversion_status,
'更新时间' => $doc->updated_at->format('Y-m-d H:i:s'),
'卡住时长' => $doc->updated_at->diffForHumans(),
];
})->toArray();
$this->table(
['ID', '标题', '状态', '更新时间', '卡住时长'],
$tableData
);
if ($dryRun) {
$this->info('');
$this->info('这是预览模式,没有实际修改任何数据');
$this->info('移除 --dry-run 选项以执行修复');
return self::SUCCESS;
}
// 确认操作
if (!$this->confirm('是否要将这些文档标记为失败状态?', true)) {
$this->info('操作已取消');
return self::SUCCESS;
}
// 修复文档
$fixed = 0;
foreach ($stuckDocuments as $document) {
try {
$document->update([
'conversion_status' => 'failed',
'conversion_error' => "转换任务超时(卡在 {$status} 状态超过 {$timeout} 分钟)",
]);
$fixed++;
$this->line("✓ 已修复: [{$document->id}] {$document->title}");
} catch (\Exception $e) {
$this->error("✗ 修复失败: [{$document->id}] {$document->title} - {$e->getMessage()}");
}
}
$this->info('');
$this->info("修复完成!共修复 {$fixed} 个文档");
$this->info('现在可以在管理界面使用"重试转换"功能重新处理这些文档');
return self::SUCCESS;
}
}

View File

@@ -226,12 +226,20 @@ class DocumentResource extends Resource
->label('重试转换')
->icon('heroicon-o-arrow-path')
->color('warning')
->visible(fn (Document $record): bool => $record->conversion_status === 'failed')
->visible(fn (Document $record): bool =>
in_array($record->conversion_status, ['failed', 'processing', 'pending'])
)
->requiresConfirmation()
->modalHeading('重试文档转换')
->modalDescription(fn (Document $record): string =>
'确定要重新转换文档 "' . $record->title . '" 吗?' .
($record->conversion_error ? "\n\n上次失败原因" . $record->conversion_error : '')
"\n\n当前状态" . match($record->conversion_status) {
'failed' => '转换失败',
'processing' => '转换中(可能卡住)',
'pending' => '等待转换',
default => $record->conversion_status,
} .
($record->conversion_error ? "\n\n错误信息:" . $record->conversion_error : '')
)
->modalSubmitActionLabel('确认重试')
->action(function (Document $record) {