repository = $repository ?? new EventQueueRepository(); $this->execution_engine = $execution_engine ?? new ExecutionEngine(); } /** * Process pending events from the queue. * * @param int $limit Max events to process per run. * @return int Number of events processed. */ public function process(int $limit = 20): int { $jobs = $this->repository->fetchPending($limit); $count = 0; foreach ($jobs as $job) { $this->repository->markProcessing((int) $job->id); try { $event = $this->reconstructEvent((string) $job->event_name, (string) $job->payload_json, (string) $job->created_at); if (null === $event) { Log::warning('EventWorker: could not reconstruct event from job', array( 'event_name' => $job->event_name, 'job_id' => $job->id, )); $this->repository->markFailed((int) $job->id); continue; } $local = $this->execution_engine->start_from_event($event); if (empty($local['matched'])) { Log::info('EventWorker: no matching workflows for event', array( 'event_name' => $job->event_name, 'job_id' => $job->id, )); } $this->repository->markProcessed((int) $job->id); $count++; } catch (\Throwable $e) { Log::error('EventWorker: processing failed (general error)', array( 'job_id' => $job->id, 'message' => $e->getMessage(), )); $this->repository->markFailed((int) $job->id); } } return $count; } /** * Reconstruct Event object from stored data. * * @param string $event_name Event name. * @param string $payload_json JSON payload. * @param string $queued_created_at Fallback queue creation timestamp. * @return Event|null */ private function reconstructEvent(string $event_name, string $payload_json, string $queued_created_at): ?Event { $payload = json_decode($payload_json, true); if (! is_array($payload)) { return null; } if (! isset($payload['event_class'])) { return null; } $class = $payload['event_class']; $occurred_at = isset($payload['occurred_at']) && '' !== (string) $payload['occurred_at'] ? (string) $payload['occurred_at'] : $queued_created_at; if (in_array($class, [OrderCreatedEvent::class, PaymentCompletedEvent::class])) { $order_id = $payload['order_id'] ?? 0; return $order_id > 0 ? new $class((int) $order_id, $occurred_at) : null; } if (OrderStatusChangedEvent::class === $class) { $order_id = $payload['order_id'] ?? 0; $old_status = $payload['old_status'] ?? ''; $new_status = $payload['new_status'] ?? ''; return $order_id > 0 ? new OrderStatusChangedEvent((int) $order_id, $old_status, $new_status, $occurred_at) : null; } if (CartAbandonedEvent::class === $class) { $abandoned_cart_id = $payload['abandoned_cart_id'] ?? 0; $cart_data = $payload['cart_data'] ?? array(); return $abandoned_cart_id > 0 ? new CartAbandonedEvent((int) $abandoned_cart_id, $cart_data, $occurred_at) : null; } $triggers = WorkflowTriggers::getTriggers(); if (isset($triggers[$event_name])) { return new WorkflowTriggerEvent($event_name, $payload, $occurred_at); } return null; } }