diffInSeconds($cached['stored_at']) > self::TTL_SECONDS) { return null; } return $cached['body']; } public function put(array $body): array { $cached = Cache::get(self::FULL_RESPONSE_KEY); $cachedBody = is_array($cached['body'] ?? null) ? $cached['body'] : null; if ($cachedBody !== null && PriceBroadcastService::lastPriceIdentity($cachedBody) === PriceBroadcastService::lastPriceIdentity($body)) { Cache::put(self::FULL_RESPONSE_KEY, [ 'body' => $cachedBody, 'stored_at' => now(), ], now()->addSeconds(self::TTL_SECONDS + 5)); return $cachedBody; } Cache::put(self::FULL_RESPONSE_KEY, [ 'body' => $body, 'stored_at' => now(), ], now()->addSeconds(self::TTL_SECONDS + 5)); app(ProductDisplayPriceService::class)->cacheLivePricePayload($body); app(PriceBroadcastService::class)->broadcastLastIfChanged($body); return $body; } public function markTick(): void { Cache::put(self::LAST_TICK_KEY, now(), now()->addMinutes(10)); } public function hasRecentTick(): bool { $lastTick = Cache::get(self::LAST_TICK_KEY); if ($lastTick === null) { return false; } return now()->diffInSeconds($lastTick) <= self::TICK_MAX_AGE_SECONDS; } /** پاسخ سریع بدون API خارجی — فقط وقتی کران اخیراً tick زده (همان فرمول lastPrice) */ public function buildFromDatabase(): array { $config = app(ConfigService::class); $display = app(ProductDisplayPriceService::class); $last = Price::orderByDesc('id')->first(); $base = $last ? (float) $last->price : 0.0; $provider = $config->priceProvider(); if ($base <= 0) { $payload = [ 'sell' => 0, 'buy' => 0, 'price_last' => 0, 'price' => 0, 'main_product' => 0, 'montzar' => 0, 'zaarbox' => 0, 'closed' => false, 'status' => 'no_quote', 'time' => now(), ]; } else { $montzarSource = 0; $zaarboxSource = 0; $mainProductSource = 0; $priceSource = 'database_last'; if ($provider === 'montzar') { $liveMontzar = (float) $config->get('montzar_live_price', 0); if ($liveMontzar > 0) { $base = $liveMontzar; } $montzarSource = (int) ($liveMontzar > 0 ? $liveMontzar : $base); $priceSource = 'montzar'; } elseif ($provider === 'zaarbox') { $zaarboxSource = (int) $base; } elseif ($provider === 'main_product') { $mainProductSource = (int) $base; } $trade = $config->buildTradeDisplayPrices($base, $priceSource); $payload = [ 'sell' => $trade['sell'], 'buy' => $trade['buy'], 'price_last' => $trade['priceLast'], 'price' => (int) $base, 'main_product' => $mainProductSource, 'montzar' => $montzarSource, 'zaarbox' => $zaarboxSource, 'closed' => false, 'status' => 'open', 'time' => now(), ]; } $payload['up'] = $config->resolvedDailyBaseExtremes()['up'] > 0 ? [ 'sell' => max(0, (int) ceil($config->resolvedDailyBaseExtremes()['down'] - $config->displaySellSpreadRial())), 'buy' => (int) ceil($config->resolvedDailyBaseExtremes()['up'] + $config->displayBuySpreadRial()), ] : ['sell' => $payload['sell'] ?? 0, 'buy' => $payload['buy'] ?? 0]; $payload['last_historical'] = $display->resolveHistoricalBasePrice(); return $payload; } }