added ons

This commit is contained in:
2026-07-12 20:58:34 +03:30
parent 555f09b9c7
commit 300c909b9e
6 changed files with 85 additions and 25 deletions
@@ -7,6 +7,7 @@ use App\Services\AssetSummaryService;
use App\Services\LivePriceCacheService; use App\Services\LivePriceCacheService;
use App\Services\PriceBroadcastService; use App\Services\PriceBroadcastService;
use App\Services\PriceStreamService; use App\Services\PriceStreamService;
use App\Services\ZaarboxPriceService;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\StreamedResponse; use Symfony\Component\HttpFoundation\StreamedResponse;
@@ -47,7 +48,7 @@ class PriceStreamController extends Controller
$cache = app(LivePriceCacheService::class); $cache = app(LivePriceCacheService::class);
$last = $cache->get() ?? $cache->buildFromDatabase(); $last = $cache->get() ?? $cache->buildFromDatabase();
if (is_array($last) && $last !== []) { if (is_array($last) && $last !== []) {
$send('last', $last); $send('last', app(ZaarboxPriceService::class)->attachGoldOunceToPayload($last));
} }
$list = app(PriceBroadcastService::class)->buildListPayload(false); $list = app(PriceBroadcastService::class)->buildListPayload(false);
+4 -20
View File
@@ -167,25 +167,7 @@ class ReportController extends Controller
protected function resolveGoldOuncePayload(): ?array protected function resolveGoldOuncePayload(): ?array
{ {
$config = app(ConfigService::class); return app(ZaarboxPriceService::class)->clientGoldOuncePayload();
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'] ?? 'انس طلا')) ?: 'انس طلا',
];
} }
public function lastPrice(Request $request) public function lastPrice(Request $request)
@@ -197,6 +179,8 @@ class ReportController extends Controller
if (! $forceRefresh) { if (! $forceRefresh) {
$cached = $cache->get(); $cached = $cache->get();
if ($cached !== null) { if ($cached !== null) {
$cached['gold_ounce'] = $this->resolveGoldOuncePayload();
return $cached; return $cached;
} }
@@ -204,7 +188,7 @@ class ReportController extends Controller
if ($cache->hasRecentTick()) { if ($cache->hasRecentTick()) {
$dbBuilt = $cache->buildFromDatabase(); $dbBuilt = $cache->buildFromDatabase();
if (($dbBuilt['price'] ?? 0) > 0) { if (($dbBuilt['price'] ?? 0) > 0) {
return $cache->put($dbBuilt); return $this->appendUpPrices($dbBuilt, false);
} }
} }
} }
+1 -1
View File
@@ -139,6 +139,6 @@ class LivePriceCacheService
: ['sell' => $payload['sell'] ?? 0, 'buy' => $payload['buy'] ?? 0]; : ['sell' => $payload['sell'] ?? 0, 'buy' => $payload['buy'] ?? 0];
$payload['last_historical'] = $display->resolveHistoricalBasePrice(); $payload['last_historical'] = $display->resolveHistoricalBasePrice();
return $payload; return app(ZaarboxPriceService::class)->attachGoldOunceToPayload($payload);
} }
} }
+9
View File
@@ -33,6 +33,10 @@ class PriceBroadcastService
$lastPayload = $cache->get() ?? $cache->buildFromDatabase(); $lastPayload = $cache->get() ?? $cache->buildFromDatabase();
} }
if (is_array($lastPayload) && $lastPayload !== []) {
$lastPayload = app(ZaarboxPriceService::class)->attachGoldOunceToPayload($lastPayload);
}
if ($force) { if ($force) {
if (is_array($lastPayload) && $lastPayload !== []) { if (is_array($lastPayload) && $lastPayload !== []) {
$stream->publish('last', $lastPayload); $stream->publish('last', $lastPayload);
@@ -63,6 +67,8 @@ class PriceBroadcastService
*/ */
public static function lastPriceIdentity(array $payload): array public static function lastPriceIdentity(array $payload): array
{ {
$goldOunce = is_array($payload['gold_ounce'] ?? null) ? $payload['gold_ounce'] : null;
return [ return [
'sell' => (int) ($payload['sell'] ?? 0), 'sell' => (int) ($payload['sell'] ?? 0),
'buy' => (int) ($payload['buy'] ?? 0), 'buy' => (int) ($payload['buy'] ?? 0),
@@ -72,6 +78,7 @@ class PriceBroadcastService
'main_product' => (int) ($payload['main_product'] ?? 0), 'main_product' => (int) ($payload['main_product'] ?? 0),
'montzar' => (int) ($payload['montzar'] ?? 0), 'montzar' => (int) ($payload['montzar'] ?? 0),
'zaarbox' => (int) ($payload['zaarbox'] ?? 0), 'zaarbox' => (int) ($payload['zaarbox'] ?? 0),
'gold_ounce_price' => round((float) ($goldOunce['price'] ?? 0), 2),
]; ];
} }
@@ -86,6 +93,8 @@ class PriceBroadcastService
return; return;
} }
$lastPayload = app(ZaarboxPriceService::class)->attachGoldOunceToPayload($lastPayload);
if (! $this->rememberHashIfChanged(self::LAST_HASH_KEY, self::lastPriceIdentity($lastPayload))) { if (! $this->rememberHashIfChanged(self::LAST_HASH_KEY, self::lastPriceIdentity($lastPayload))) {
return; return;
} }
+67 -3
View File
@@ -59,6 +59,15 @@ class ZaarboxPriceService
return trim($this->config->getString('zaarbox_api_url', '')) !== ''; 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) */ /** کلید یا id ردیف در JSON تابلو (settings: zaarbox_price_key) */
public function priceIdentifier(): string public function priceIdentifier(): string
{ {
@@ -201,15 +210,29 @@ class ZaarboxPriceService
*/ */
public function findOunceFeedRow(array $prices): ?array public function findOunceFeedRow(array $prices): ?array
{ {
$identifier = $this->ouncePriceIdentifier();
if ($identifier !== '') {
$row = $this->findFeedRow($prices, $identifier);
if ($row !== null) {
return $row;
}
}
$row = collect($prices) $row = collect($prices)
->map(fn ($item) => is_array($item) ? $item : (array) $item) ->map(fn ($item) => is_array($item) ? $item : (array) $item)
->filter(function (array $item) { ->filter(function (array $item) {
$name = trim((string) ($item['name'] ?? '')); $name = trim((string) ($item['name'] ?? ''));
return $name !== '' && mb_strpos($name, 'انس') !== false; return $name !== '' && (
mb_strpos($name, 'انس') !== false
|| mb_strpos($name, 'اونس') !== false
);
}) })
->sortBy([ ->sortBy([
fn (array $item) => trim((string) ($item['key'] ?? '')) === 'tala_ounce' ? 0 : 1,
fn (array $item) => (bool) ($item['active'] ?? false) ? 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) => str_contains(trim((string) ($item['name'] ?? '')), 'انس طلا') ? 0 : 1,
fn (array $item) => mb_strlen(trim((string) ($item['name'] ?? ''))), fn (array $item) => mb_strlen(trim((string) ($item['name'] ?? ''))),
]) ])
@@ -229,7 +252,11 @@ class ZaarboxPriceService
} }
try { 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)) { if (! is_array($prices)) {
return $empty; return $empty;
} }
@@ -246,7 +273,7 @@ class ZaarboxPriceService
return [ return [
'price' => $price, 'price' => $price,
'name' => trim((string) ($row['name'] ?? 'انس طلا')), 'name' => trim((string) ($row['name'] ?? 'اونس طلا')) ?: 'اونس طلا',
'raw' => $row, 'raw' => $row,
]; ];
} catch (\Throwable $e) { } 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<string, mixed> $payload
* @return array<string, mixed>
*/
public function attachGoldOunceToPayload(array $payload): array
{
$payload['gold_ounce'] = $this->clientGoldOuncePayload();
return $payload;
}
/** /**
* @return array{price: int, order_price: int, raw: array<string, mixed>|null} * @return array{price: int, order_price: int, raw: array<string, mixed>|null}
*/ */
+2
View File
@@ -12,6 +12,8 @@ return [
'price_provider' => 'montzar', 'price_provider' => 'montzar',
'zaarbox_api_url' => 'https://price.zaarbox.ir/api/free', 'zaarbox_api_url' => 'https://price.zaarbox.ir/api/free',
'zaarbox_price_key' => '', 'zaarbox_price_key' => '',
/** key ردیف انس در JSON تابلو — پیش‌فرض tala_ounce (tala.ir) */
'zaarbox_ounce_key' => 'tala_ounce',
/** UUID نمودار Zaarbox — خالی = پیش‌فرض ca3800a0-... در ZaarboxPriceService */ /** UUID نمودار Zaarbox — خالی = پیش‌فرض ca3800a0-... در ZaarboxPriceService */
'chart_id' => '', 'chart_id' => '',
/** false = نمودار از Zaarbox (chart_id) | true = داده ذخیره‌شده خود سایت */ /** false = نمودار از Zaarbox (chart_id) | true = داده ذخیره‌شده خود سایت */