TenantAtlas/app/Models/BulkOperationRun.php
Ahmed Darrazi 0b0c2b70b9 feat: 053 unify runs monitoring
- Add statusBucket/runType semantics for BulkOperationRun\n- Add Monitoring/Operations hub UI (view-only) with filters + related links\n- Add Drift run context + lifecycle notifications and guardrails\n- Add Pest coverage for status buckets and drift notifications
2026-01-16 16:04:33 +01:00

105 lines
2.4 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class BulkOperationRun extends Model
{
use HasFactory;
protected $fillable = [
'tenant_id',
'user_id',
'resource',
'action',
'idempotency_key',
'status',
'total_items',
'processed_items',
'succeeded',
'failed',
'skipped',
'item_ids',
'failures',
'audit_log_id',
];
protected $casts = [
'item_ids' => 'array',
'failures' => 'array',
'processed_items' => 'integer',
'total_items' => 'integer',
'succeeded' => 'integer',
'failed' => 'integer',
'skipped' => 'integer',
];
public function tenant(): BelongsTo
{
return $this->belongsTo(Tenant::class);
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function auditLog(): BelongsTo
{
return $this->belongsTo(AuditLog::class);
}
public function runType(): string
{
return "{$this->resource}.{$this->action}";
}
public function statusBucket(): string
{
$status = $this->status;
if ($status === 'pending') {
return 'queued';
}
if ($status === 'running') {
return 'running';
}
$succeededCount = (int) ($this->succeeded ?? 0);
$failedCount = (int) ($this->failed ?? 0);
$failureEntries = $this->failures ?? [];
$hasNonSkippedFailure = false;
foreach ($failureEntries as $entry) {
if (! is_array($entry)) {
continue;
}
if (($entry['type'] ?? 'failed') !== 'skipped') {
$hasNonSkippedFailure = true;
break;
}
}
$hasFailures = $failedCount > 0 || $hasNonSkippedFailure;
if ($succeededCount > 0 && $hasFailures) {
return 'partially succeeded';
}
if ($succeededCount === 0 && $hasFailures) {
return 'failed';
}
return match ($status) {
'completed', 'completed_with_errors' => 'succeeded',
'failed', 'aborted' => 'failed',
default => 'failed',
};
}
}