diff --git a/app/Http/Controllers/Site/PriceStreamController.php b/app/Http/Controllers/Site/PriceStreamController.php index bc2584d..bea6706 100644 --- a/app/Http/Controllers/Site/PriceStreamController.php +++ b/app/Http/Controllers/Site/PriceStreamController.php @@ -7,6 +7,7 @@ use App\Services\AssetSummaryService; use App\Services\LivePriceCacheService; use App\Services\PriceBroadcastService; use App\Services\PriceStreamService; +use App\Services\ZaarboxPriceService; use Illuminate\Http\Request; use Symfony\Component\HttpFoundation\StreamedResponse; @@ -47,7 +48,7 @@ class PriceStreamController extends Controller $cache = app(LivePriceCacheService::class); $last = $cache->get() ?? $cache->buildFromDatabase(); if (is_array($last) && $last !== []) { - $send('last', $last); + $send('last', app(ZaarboxPriceService::class)->attachGoldOunceToPayload($last)); } $list = app(PriceBroadcastService::class)->buildListPayload(false); diff --git a/app/Http/Controllers/Site/ReportController.php b/app/Http/Controllers/Site/ReportController.php index 65de76f..ac21fe0 100644 --- a/app/Http/Controllers/Site/ReportController.php +++ b/app/Http/Controllers/Site/ReportController.php @@ -167,25 +167,7 @@ class ReportController extends Controller protected function resolveGoldOuncePayload(): ?array { - $config = app(ConfigService::class); - if (! $config->getBool('show_gold_ounce_price', false)) { - return null; - } - - $zaarbox = app(ZaarboxPriceService::class); - if (! $zaarbox->hasApiUrlConfigured()) { - return null; - } - - $ounce = $zaarbox->fetchOuncePrice(); - if (($ounce['price'] ?? 0) <= 0) { - return null; - } - - return [ - 'price' => (int) $ounce['price'], - 'label' => trim((string) ($ounce['name'] ?? 'انس طلا')) ?: 'انس طلا', - ]; + return app(ZaarboxPriceService::class)->clientGoldOuncePayload(); } public function lastPrice(Request $request) @@ -197,6 +179,8 @@ class ReportController extends Controller if (! $forceRefresh) { $cached = $cache->get(); if ($cached !== null) { + $cached['gold_ounce'] = $this->resolveGoldOuncePayload(); + return $cached; } @@ -204,7 +188,7 @@ class ReportController extends Controller if ($cache->hasRecentTick()) { $dbBuilt = $cache->buildFromDatabase(); if (($dbBuilt['price'] ?? 0) > 0) { - return $cache->put($dbBuilt); + return $this->appendUpPrices($dbBuilt, false); } } } diff --git a/app/Services/LivePriceCacheService.php b/app/Services/LivePriceCacheService.php index 83c66ce..0e2ee7e 100644 --- a/app/Services/LivePriceCacheService.php +++ b/app/Services/LivePriceCacheService.php @@ -139,6 +139,6 @@ class LivePriceCacheService : ['sell' => $payload['sell'] ?? 0, 'buy' => $payload['buy'] ?? 0]; $payload['last_historical'] = $display->resolveHistoricalBasePrice(); - return $payload; + return app(ZaarboxPriceService::class)->attachGoldOunceToPayload($payload); } } diff --git a/app/Services/PriceBroadcastService.php b/app/Services/PriceBroadcastService.php index 9d1b85f..fd3f8d2 100644 --- a/app/Services/PriceBroadcastService.php +++ b/app/Services/PriceBroadcastService.php @@ -33,6 +33,10 @@ class PriceBroadcastService $lastPayload = $cache->get() ?? $cache->buildFromDatabase(); } + if (is_array($lastPayload) && $lastPayload !== []) { + $lastPayload = app(ZaarboxPriceService::class)->attachGoldOunceToPayload($lastPayload); + } + if ($force) { if (is_array($lastPayload) && $lastPayload !== []) { $stream->publish('last', $lastPayload); @@ -63,6 +67,8 @@ class PriceBroadcastService */ public static function lastPriceIdentity(array $payload): array { + $goldOunce = is_array($payload['gold_ounce'] ?? null) ? $payload['gold_ounce'] : null; + return [ 'sell' => (int) ($payload['sell'] ?? 0), 'buy' => (int) ($payload['buy'] ?? 0), @@ -72,6 +78,7 @@ class PriceBroadcastService 'main_product' => (int) ($payload['main_product'] ?? 0), 'montzar' => (int) ($payload['montzar'] ?? 0), 'zaarbox' => (int) ($payload['zaarbox'] ?? 0), + 'gold_ounce_price' => round((float) ($goldOunce['price'] ?? 0), 2), ]; } @@ -86,6 +93,8 @@ class PriceBroadcastService return; } + $lastPayload = app(ZaarboxPriceService::class)->attachGoldOunceToPayload($lastPayload); + if (! $this->rememberHashIfChanged(self::LAST_HASH_KEY, self::lastPriceIdentity($lastPayload))) { return; } diff --git a/app/Services/ZaarboxPriceService.php b/app/Services/ZaarboxPriceService.php index 364d5c7..e242766 100644 --- a/app/Services/ZaarboxPriceService.php +++ b/app/Services/ZaarboxPriceService.php @@ -59,6 +59,15 @@ class ZaarboxPriceService return trim($this->config->getString('zaarbox_api_url', '')) !== ''; } + /** کلید یا id ردیف انس در JSON تابلو (settings: zaarbox_ounce_key) */ + public function ouncePriceIdentifier(): string + { + return trim((string) $this->config->getString( + 'zaarbox_ounce_key', + (string) config('price_providers.zaarbox_ounce_key', 'tala_ounce') + )); + } + /** کلید یا id ردیف در JSON تابلو (settings: zaarbox_price_key) */ public function priceIdentifier(): string { @@ -201,15 +210,29 @@ class ZaarboxPriceService */ public function findOunceFeedRow(array $prices): ?array { + $identifier = $this->ouncePriceIdentifier(); + if ($identifier !== '') { + $row = $this->findFeedRow($prices, $identifier); + if ($row !== null) { + return $row; + } + } + $row = collect($prices) ->map(fn ($item) => is_array($item) ? $item : (array) $item) ->filter(function (array $item) { $name = trim((string) ($item['name'] ?? '')); - return $name !== '' && mb_strpos($name, 'انس') !== false; + return $name !== '' && ( + mb_strpos($name, 'انس') !== false + || mb_strpos($name, 'اونس') !== false + ); }) ->sortBy([ + fn (array $item) => trim((string) ($item['key'] ?? '')) === 'tala_ounce' ? 0 : 1, fn (array $item) => (bool) ($item['active'] ?? false) ? 0 : 1, + fn (array $item) => trim((string) ($item['category'] ?? '')) === 'ounce' ? 0 : 1, + fn (array $item) => str_contains(trim((string) ($item['name'] ?? '')), 'اونس طلا') ? 0 : 1, fn (array $item) => str_contains(trim((string) ($item['name'] ?? '')), 'انس طلا') ? 0 : 1, fn (array $item) => mb_strlen(trim((string) ($item['name'] ?? ''))), ]) @@ -229,7 +252,11 @@ class ZaarboxPriceService } try { - $prices = $this->cachedPriceList(false) ?? $this->fetchPriceList(); + // مثل fetchPrice — اول HTTP تازه؛ کش فقط fallback (کش خالی/قدیمی باعث null شدن gold_ounce می‌شد) + $prices = $this->fetchPriceList(); + if (! is_array($prices) || $prices === []) { + $prices = $this->cachedPriceList(false); + } if (! is_array($prices)) { return $empty; } @@ -246,7 +273,7 @@ class ZaarboxPriceService return [ 'price' => $price, - 'name' => trim((string) ($row['name'] ?? 'انس طلا')), + 'name' => trim((string) ($row['name'] ?? 'اونس طلا')) ?: 'اونس طلا', 'raw' => $row, ]; } catch (\Throwable $e) { @@ -256,6 +283,43 @@ class ZaarboxPriceService } } + /** payload انس برای last/SSE — null یعنی نمایش خاموش یا بدون مظنه */ + public function clientGoldOuncePayload(): ?array + { + if (! $this->config->getBool('show_gold_ounce_price', false)) { + return null; + } + + if (! $this->hasApiUrlConfigured()) { + return null; + } + + $ounce = $this->fetchOuncePrice(); + if (($ounce['price'] ?? 0) <= 0) { + return null; + } + + $rawPrice = is_array($ounce['raw'] ?? null) + ? ($ounce['raw']['price'] ?? $ounce['price']) + : $ounce['price']; + + return [ + 'price' => round((float) $rawPrice, 2), + 'label' => trim((string) ($ounce['name'] ?? 'اونس طلا')) ?: 'اونس طلا', + ]; + } + + /** + * @param array $payload + * @return array + */ + public function attachGoldOunceToPayload(array $payload): array + { + $payload['gold_ounce'] = $this->clientGoldOuncePayload(); + + return $payload; + } + /** * @return array{price: int, order_price: int, raw: array|null} */ diff --git a/config/price_providers.php b/config/price_providers.php index b545ce6..b18360e 100644 --- a/config/price_providers.php +++ b/config/price_providers.php @@ -12,6 +12,8 @@ return [ 'price_provider' => 'montzar', 'zaarbox_api_url' => 'https://price.zaarbox.ir/api/free', 'zaarbox_price_key' => '', + /** key ردیف انس در JSON تابلو — پیش‌فرض tala_ounce (tala.ir) */ + 'zaarbox_ounce_key' => 'tala_ounce', /** UUID نمودار Zaarbox — خالی = پیش‌فرض ca3800a0-... در ZaarboxPriceService */ 'chart_id' => '', /** false = نمودار از Zaarbox (chart_id) | true = داده ذخیره‌شده خود سایت */