mirror of
https://gh.wpcy.net/https://github.com/hayBIT/freescout2ant.git
synced 2026-07-15 12:13:04 +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
72 lines
2 KiB
PHP
72 lines
2 KiB
PHP
<?php
|
|
|
|
namespace Modules\AmeiseModule\Entities;
|
|
|
|
use App\Conversation;
|
|
use App\Thread;
|
|
use App\User;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
|
|
class CrmArchiveAttempt extends Model
|
|
{
|
|
const STATUS_SUCCESS = 'success';
|
|
const STATUS_PENDING = 'pending';
|
|
const STATUS_FAILED_NO_CUSTOMER = 'failed_no_customer';
|
|
const STATUS_FAILED_AMBIGUOUS_CUSTOMER = 'failed_ambiguous_customer';
|
|
const STATUS_FAILED_API = 'failed_api';
|
|
const STATUS_FAILED_TOKEN = 'failed_token';
|
|
const STATUS_FAILED_ATTACHMENT = 'failed_attachment';
|
|
const STATUS_FAILED_EXCEPTION = 'failed_exception';
|
|
|
|
const FAILURE_STATUSES = [
|
|
self::STATUS_FAILED_NO_CUSTOMER,
|
|
self::STATUS_FAILED_AMBIGUOUS_CUSTOMER,
|
|
self::STATUS_FAILED_API,
|
|
self::STATUS_FAILED_TOKEN,
|
|
self::STATUS_FAILED_ATTACHMENT,
|
|
self::STATUS_FAILED_EXCEPTION,
|
|
];
|
|
|
|
protected $table = 'crm_archive_attempts';
|
|
protected $guarded = ['id'];
|
|
protected $dates = ['next_retry_at', 'resolved_at', 'created_at', 'updated_at'];
|
|
|
|
public function conversation()
|
|
{
|
|
return $this->belongsTo(Conversation::class);
|
|
}
|
|
|
|
public function thread()
|
|
{
|
|
return $this->belongsTo(Thread::class, 'thread_id');
|
|
}
|
|
|
|
public function user()
|
|
{
|
|
return $this->belongsTo(User::class, 'user_id');
|
|
}
|
|
|
|
public static function record(array $data): self
|
|
{
|
|
$threadId = $data['thread_id'] ?? null;
|
|
$userId = $data['user_id'] ?? null;
|
|
|
|
$maxAttempt = static::where('thread_id', $threadId)
|
|
->where('user_id', $userId)
|
|
->max('attempt_no');
|
|
|
|
$data['attempt_no'] = (int) ($maxAttempt ?? 0) + 1;
|
|
|
|
$attempt = static::create($data);
|
|
|
|
if (($data['status'] ?? null) === self::STATUS_SUCCESS) {
|
|
static::where('thread_id', $threadId)
|
|
->where('user_id', $userId)
|
|
->whereNull('resolved_at')
|
|
->where('id', '!=', $attempt->id)
|
|
->update(['resolved_at' => now()]);
|
|
}
|
|
|
|
return $attempt;
|
|
}
|
|
}
|