'integer', 'started_at' => 'datetime', 'completed_at' => 'datetime', 'duration_ms' => 'integer', 'total_steps' => 'integer', 'completed_steps' => 'integer', 'failed_step' => 'integer', 'execution_log' => 'array', 'input_variables' => 'array', 'executed_by' => 'integer', ]; /** * 상태별 필터링 */ public function scopeStatus($query, string $status) { return $query->where('status', $status); } /** * 성공한 실행만 조회 */ public function scopeSuccessful($query) { return $query->where('status', self::STATUS_SUCCESS); } /** * 실패한 실행만 조회 */ public function scopeFailed($query) { return $query->where('status', self::STATUS_FAILED); } /** * 관계: 플로우 */ public function flow(): BelongsTo { return $this->belongsTo(AdminApiFlow::class, 'flow_id'); } /** * 관계: 실행자 */ public function executor(): BelongsTo { return $this->belongsTo(User::class, 'executed_by'); } /** * 실행 중인지 확인 */ public function isRunning(): bool { return $this->status === self::STATUS_RUNNING; } /** * 완료되었는지 확인 (성공/실패/부분 포함) */ public function isCompleted(): bool { return in_array($this->status, [ self::STATUS_SUCCESS, self::STATUS_FAILED, self::STATUS_PARTIAL, ]); } /** * 진행률 반환 (0-100) */ public function getProgressAttribute(): int { if (! $this->total_steps || $this->total_steps === 0) { return 0; } return (int) round(($this->completed_steps / $this->total_steps) * 100); } /** * 상태 라벨 반환 (한글) */ public function getStatusLabelAttribute(): string { return match ($this->status) { self::STATUS_PENDING => '대기 중', self::STATUS_RUNNING => '실행 중', self::STATUS_SUCCESS => '성공', self::STATUS_FAILED => '실패', self::STATUS_PARTIAL => '부분 성공', default => $this->status, }; } /** * 상태 색상 반환 (Tailwind CSS class) */ public function getStatusColorAttribute(): string { return match ($this->status) { self::STATUS_PENDING => 'badge-ghost', self::STATUS_RUNNING => 'badge-info', self::STATUS_SUCCESS => 'badge-success', self::STATUS_FAILED => 'badge-error', self::STATUS_PARTIAL => 'badge-warning', default => 'badge-ghost', }; } }