table = $resource->getTableName('esun_lingxing_shipment_sync'); } public function enqueue(array $payload, string $requestId = ''): void { $connection = $this->resource->getConnection(); $externalKey = (string)$payload['external_key']; $existing = $connection->fetchRow($connection->select()->from($this->table) ->where('external_key = ?', $externalKey)->limit(1)); $data = [ 'global_order_no' => (string)($payload['global_order_no'] ?? ''), 'wo_id' => !empty($payload['wo_id']) ? (int)$payload['wo_id'] : null, 'wo_number' => (string)$payload['wo_number'], 'package_no' => (string)($payload['package_no'] ?? '') ?: null, 'tracking_no' => implode(',', array_values(array_filter(array_column($payload['tracks'] ?? [], 'number')))), 'request_id' => $requestId ?: null, 'payload_json' => (string)json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR), 'updated_at' => gmdate('Y-m-d H:i:s'), ]; if (!$existing) { $data += [ 'external_key' => $externalKey, 'status' => 'pending', 'created_at' => gmdate('Y-m-d H:i:s'), ]; $connection->insert($this->table, $data); return; } if ((string)$existing['status'] !== 'success') { $connection->update($this->table, $data, ['entity_id = ?' => (int)$existing['entity_id']]); } } public function getDue(int $limit): array { $connection = $this->resource->getConnection(); return $connection->fetchAll($connection->select()->from($this->table) ->where('status IN (?)', ['pending', 'retrying']) ->where('next_retry_at IS NULL OR next_retry_at <= ?', gmdate('Y-m-d H:i:s')) ->order('entity_id ASC')->limit($limit)); } public function claim(int $entityId): bool { return $this->resource->getConnection()->update($this->table, [ 'status' => 'processing', 'last_attempt_at' => gmdate('Y-m-d H:i:s'), ], ['entity_id = ?' => $entityId, 'status IN (?)' => ['pending', 'retrying']]) === 1; } public function markSuccess(int $entityId, int $orderId, string $incrementId, int $shipmentId): void { $now = gmdate('Y-m-d H:i:s'); $this->resource->getConnection()->update($this->table, [ 'order_id' => $orderId, 'increment_id' => $incrementId, 'shipment_id' => $shipmentId, 'status' => 'success', 'last_error_code' => null, 'last_error_message' => null, 'next_retry_at' => null, 'synced_at' => $now, 'updated_at' => $now, ], ['entity_id = ?' => $entityId]); } public function markFailure(int $entityId, int $attemptCount, int $maxAttempts, string $code, string $message): string { $attemptCount++; $status = $attemptCount >= $maxAttempts ? 'failed' : 'retrying'; $delay = min(3600, 30 * (2 ** min(7, max(0, $attemptCount - 1)))); $this->resource->getConnection()->update($this->table, [ 'status' => $status, 'attempt_count' => $attemptCount, 'next_retry_at' => $status === 'retrying' ? gmdate('Y-m-d H:i:s', time() + $delay) : null, 'last_error_code' => $code ?: null, 'last_error_message' => mb_substr($message, 0, 65535), 'updated_at' => gmdate('Y-m-d H:i:s'), ], ['entity_id = ?' => $entityId]); return $status; } public function retry(int $entityId): bool { return $this->resource->getConnection()->update($this->table, [ 'status' => 'pending', 'attempt_count' => 0, 'next_retry_at' => null, 'last_error_code' => null, 'last_error_message' => null, 'updated_at' => gmdate('Y-m-d H:i:s'), ], ['entity_id = ?' => $entityId, 'status IN (?)' => ['failed', 'retrying']]) === 1; } }