2026-06-13 17:35:40 +03:30
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
|
|
namespace App\Services;
|
|
|
|
|
|
|
|
|
|
|
|
use App\Http\Controllers\Site\ReportController;
|
2026-06-17 12:12:35 +03:30
|
|
|
|
use App\Models\PriceSyncLog;
|
2026-06-13 17:35:40 +03:30
|
|
|
|
use App\Models\Product;
|
|
|
|
|
|
use Illuminate\Http\Request;
|
2026-06-14 12:33:29 +03:30
|
|
|
|
use Illuminate\Support\Facades\Cache;
|
|
|
|
|
|
use Illuminate\Support\Facades\Log;
|
2026-06-13 17:35:40 +03:30
|
|
|
|
|
|
|
|
|
|
class LivePriceTickService
|
|
|
|
|
|
{
|
2026-06-14 12:33:29 +03:30
|
|
|
|
private const LOCK_SECONDS = 25;
|
|
|
|
|
|
|
2026-06-17 12:12:35 +03:30
|
|
|
|
public function tick(): ?PriceSyncLog
|
2026-06-13 17:35:40 +03:30
|
|
|
|
{
|
2026-06-14 12:33:29 +03:30
|
|
|
|
$lock = Cache::lock('prices:tick', self::LOCK_SECONDS);
|
|
|
|
|
|
if (! $lock->get()) {
|
2026-06-17 12:12:35 +03:30
|
|
|
|
return null;
|
2026-06-13 17:35:40 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-17 12:12:35 +03:30
|
|
|
|
$syncLog = null;
|
|
|
|
|
|
|
2026-06-14 12:33:29 +03:30
|
|
|
|
try {
|
|
|
|
|
|
$config = app(ConfigService::class);
|
|
|
|
|
|
$sync = app(ProductPriceSyncService::class);
|
|
|
|
|
|
$zaarbox = app(ZaarboxPriceService::class);
|
2026-06-13 17:35:40 +03:30
|
|
|
|
|
2026-06-14 12:33:29 +03:30
|
|
|
|
/*
|
|
|
|
|
|
| ۱. montzar / zaarbox زنده + کش ۱۵ثانیهای /api/price/last
|
|
|
|
|
|
| (قبل از sync محصولات تا آبشدهٔ «قیمت اپ» تازه باشد)
|
|
|
|
|
|
*/
|
|
|
|
|
|
$request = Request::create('/api/price/last', 'GET', ['force_refresh' => '1']);
|
2026-06-14 21:09:43 +03:30
|
|
|
|
$result = app(ReportController::class)->lastPrice($request);
|
|
|
|
|
|
$payload = is_array($result)
|
|
|
|
|
|
? $result
|
|
|
|
|
|
: (json_decode($result->getContent(), true) ?: []);
|
2026-06-14 12:33:29 +03:30
|
|
|
|
if (! is_array($payload)) {
|
|
|
|
|
|
$payload = [];
|
|
|
|
|
|
}
|
2026-06-13 17:35:40 +03:30
|
|
|
|
|
2026-06-14 12:33:29 +03:30
|
|
|
|
/*
|
|
|
|
|
|
| ۲. فید zaarbox برای محصولات متغیر (reading_method)
|
|
|
|
|
|
*/
|
|
|
|
|
|
if ($zaarbox->isEnabled() || Product::query()->whereNotNull('reading_method')->exists()) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
$zaarbox->cachedPriceList(true);
|
|
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
|
Log::warning('Zaarbox feed refresh in tick failed', ['message' => $e->getMessage()]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-13 17:35:40 +03:30
|
|
|
|
|
2026-06-14 12:33:29 +03:30
|
|
|
|
/*
|
|
|
|
|
|
| ۳. همگامسازی قیمت محصولات
|
|
|
|
|
|
*/
|
|
|
|
|
|
if ($config->isStore()) {
|
|
|
|
|
|
$sync->syncStorePrices(true);
|
|
|
|
|
|
$source = 'zaarbox';
|
|
|
|
|
|
$sourceUrl = $zaarbox->apiUrl();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$sync->syncAll(true);
|
|
|
|
|
|
$source = $config->priceProvider() ?: 'legacy';
|
|
|
|
|
|
$sourceUrl = $source === 'zaarbox'
|
|
|
|
|
|
? $zaarbox->apiUrl()
|
|
|
|
|
|
: null;
|
|
|
|
|
|
}
|
2026-06-13 17:35:40 +03:30
|
|
|
|
|
2026-06-14 12:33:29 +03:30
|
|
|
|
$liquidGold = (int) ($payload['price'] ?? $config->getInt('montzar_live_price', 0));
|
|
|
|
|
|
if ($liquidGold <= 0) {
|
|
|
|
|
|
$liquidGold = app(ProductDisplayPriceService::class)->resolveHistoricalBasePrice();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$activeProducts = Product::query()
|
|
|
|
|
|
->where('active', 1)
|
|
|
|
|
|
->whereNotNull('reading_method')
|
|
|
|
|
|
->count();
|
|
|
|
|
|
|
2026-06-17 12:12:35 +03:30
|
|
|
|
$syncLog = app(PriceSyncLogService::class)->recordTick($source, $sourceUrl, $liquidGold, $activeProducts);
|
2026-06-14 12:33:29 +03:30
|
|
|
|
|
2026-06-14 13:15:45 +03:30
|
|
|
|
app(LivePriceCacheService::class)->markTick();
|
2026-06-17 12:12:35 +03:30
|
|
|
|
|
2026-06-18 19:32:28 +03:30
|
|
|
|
// last از put داخل lastPrice؛ list بعد از sync محصولات
|
|
|
|
|
|
app(PriceBroadcastService::class)->broadcastListIfChanged();
|
2026-06-14 12:33:29 +03:30
|
|
|
|
} finally {
|
|
|
|
|
|
$lock->release();
|
2026-06-13 17:35:40 +03:30
|
|
|
|
}
|
2026-06-17 12:12:35 +03:30
|
|
|
|
|
|
|
|
|
|
return $syncLog;
|
2026-06-13 17:35:40 +03:30
|
|
|
|
}
|
|
|
|
|
|
}
|