Files
2026-09-15 10:35:56 +08:00

147 lines
5.6 KiB
PHP

<?php
declare(strict_types=1);
namespace Esun\LingxingErp\Model\Sync;
use Magento\Framework\App\ResourceConnection;
use Magento\Sales\Api\Data\OrderInterface;
use Magento\Sales\Model\Order;
class OrderSyncRepository
{
private string $table;
public function __construct(private readonly ResourceConnection $resource)
{
$this->table = $resource->getTableName('esun_lingxing_order_sync');
}
public function enqueue(OrderInterface $order): void
{
$connection = $this->resource->getConnection();
$exists = $connection->fetchOne(
$connection->select()->from($this->table, ['entity_id'])->where('order_id = ?', (int)$order->getEntityId())
);
if ($exists) {
return;
}
$now = gmdate('Y-m-d H:i:s');
$connection->insert($this->table, [
'order_id' => (int)$order->getEntityId(),
'increment_id' => (string)$order->getIncrementId(),
'platform_order_no' => (string)$order->getIncrementId(),
'status' => 'pending',
'created_at' => $now,
'updated_at' => $now,
]);
}
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, string $globalOrderNo, string $requestId, string $payloadHash): void
{
$now = gmdate('Y-m-d H:i:s');
$this->resource->getConnection()->update($this->table, [
'global_order_no' => $globalOrderNo,
'status' => 'success',
'request_id' => $requestId ?: null,
'payload_hash' => $payloadHash,
'last_error_code' => null,
'last_error_message' => null,
'next_retry_at' => null,
'synced_at' => $now,
'updated_at' => $now,
], ['entity_id = ?' => $entityId]);
}
public function markCreationCanceled(int $entityId): void
{
$now = gmdate('Y-m-d H:i:s');
$this->resource->getConnection()->update($this->table, [
'status' => 'canceled',
'next_retry_at' => null,
'updated_at' => $now,
], ['entity_id = ?' => $entityId]);
}
public function markFailure(int $entityId, int $attemptCount, int $maxAttempts, string $code, string $message, string $requestId = ''): 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),
'request_id' => $requestId ?: null,
'updated_at' => gmdate('Y-m-d H:i:s'),
], ['entity_id = ?' => $entityId]);
return $status;
}
public function findByGlobalOrderNo(string $globalOrderNo): ?array
{
$connection = $this->resource->getConnection();
$row = $connection->fetchRow($connection->select()->from($this->table)
->where('global_order_no = ?', $globalOrderNo)->limit(1));
return is_array($row) ? $row : null;
}
public function findByIncrementId(string $incrementId): ?array
{
$connection = $this->resource->getConnection();
$row = $connection->fetchRow($connection->select()->from($this->table)
->where('increment_id = ?', $incrementId)->limit(1));
return is_array($row) ? $row : null;
}
/** @return string[] */
public function getTrackablePlatformOrderNos(): array
{
$connection = $this->resource->getConnection();
$salesOrderTable = $this->resource->getTableName('sales_order');
$select = $connection->select()
->from(['sync' => $this->table], ['platform_order_no'])
->joinInner(['sales_order' => $salesOrderTable], 'sales_order.entity_id = sync.order_id', [])
->where('sync.status = ?', 'success')
->where('sync.platform_order_no <> ?', '')
->where('sales_order.state NOT IN (?)', [
Order::STATE_COMPLETE,
Order::STATE_CLOSED,
Order::STATE_CANCELED,
])
->order('sync.entity_id ASC');
return array_values(array_unique(array_map('strval', $connection->fetchCol($select))));
}
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;
}
}