mirror of
https://gh.wpcy.net/https://github.com/hayBIT/freescout2ant.git
synced 2026-07-15 10:17:42 +08:00
Previously a thread could be skipped silently in ConversationArchiver (no customer match, ambiguous match, API/network error, attachment failure) without any record. Default-off logging hid the cause, and the queue job swallowed exceptions, so messages "archived" in FreeScout never surfaced as missing in Ameise. Now every archive attempt records a row in the new crm_archive_attempts table with status, sanitized HTTP response, attempt counter and resolution timestamp. ArchiveThreadsJob retries transient failures up to 5 times with exponential backoff; ameise:list-failed-archives and ameise:retry-failed-archives expose the queue from the CLI; the Ameise settings page lists open failures with retry/dismiss buttons. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VU73s4KM5JZqVWQYPpK3cP
62 lines
2.1 KiB
PHP
62 lines
2.1 KiB
PHP
<?php
|
|
|
|
namespace Modules\AmeiseModule\Console\Commands;
|
|
|
|
use Carbon\Carbon;
|
|
use Illuminate\Console\Command;
|
|
use Modules\AmeiseModule\Entities\CrmArchiveAttempt;
|
|
|
|
class ListFailedArchives extends Command
|
|
{
|
|
protected $signature = 'ameise:list-failed-archives {--since=30d : Zeitraum, z. B. 24h, 7d, 30d} {--status= : Filter auf einen konkreten Status}';
|
|
|
|
protected $description = 'Listet unaufgeloeste Archivierungs-Fehlversuche aus crm_archive_attempts';
|
|
|
|
public function handle()
|
|
{
|
|
$since = $this->parseSince((string) $this->option('since'));
|
|
$status = $this->option('status');
|
|
|
|
$query = CrmArchiveAttempt::whereNull('resolved_at')
|
|
->whereIn('status', CrmArchiveAttempt::FAILURE_STATUSES)
|
|
->where('created_at', '>=', $since);
|
|
|
|
if ($status) {
|
|
$query->where('status', $status);
|
|
}
|
|
|
|
$attempts = $query->orderBy('created_at', 'desc')->get();
|
|
|
|
if ($attempts->isEmpty()) {
|
|
$this->info('Keine offenen Fehlversuche im Zeitraum.');
|
|
return 0;
|
|
}
|
|
|
|
$rows = $attempts->map(function ($a) {
|
|
return [
|
|
'id' => $a->id,
|
|
'conv' => $a->conversation_id,
|
|
'thread' => $a->thread_id,
|
|
'user' => $a->user_id,
|
|
'status' => $a->status,
|
|
'try' => $a->attempt_no,
|
|
'reason' => mb_strimwidth((string) $a->reason, 0, 60, '...'),
|
|
'when' => $a->created_at ? $a->created_at->toDateTimeString() : '',
|
|
];
|
|
})->all();
|
|
|
|
$this->table(['ID', 'Conv', 'Thread', 'User', 'Status', 'Try', 'Reason', 'Created'], $rows);
|
|
$this->info(sprintf('%d offene Fehlversuche.', $attempts->count()));
|
|
|
|
return 0;
|
|
}
|
|
|
|
private function parseSince(string $since): Carbon
|
|
{
|
|
if (preg_match('/^(\d+)\s*([hd])$/i', trim($since), $m)) {
|
|
$n = (int) $m[1];
|
|
return strtolower($m[2]) === 'h' ? Carbon::now()->subHours($n) : Carbon::now()->subDays($n);
|
|
}
|
|
return Carbon::now()->subDays(30);
|
|
}
|
|
}
|