From 4af6689c11e07fbe6e1b525a96fa6a5d607c2a7f Mon Sep 17 00:00:00 2001 From: adonis Date: Wed, 8 Jul 2026 13:40:43 +0330 Subject: [PATCH] fix some handling 5 --- app/Console/Commands/PriceChannelTest.php | 6 + app/Services/PriceChannelBroadcastService.php | 119 ++++++++++++++---- database/sql/deploy_settings_const.sql | 2 +- ...ngs_const_preserve_value_sync_can_edit.sql | 2 +- database/sql/deposit_guide_settings.sql | 2 +- 5 files changed, 107 insertions(+), 24 deletions(-) diff --git a/app/Console/Commands/PriceChannelTest.php b/app/Console/Commands/PriceChannelTest.php index 5b32b80..9a7155a 100644 --- a/app/Console/Commands/PriceChannelTest.php +++ b/app/Console/Commands/PriceChannelTest.php @@ -22,6 +22,12 @@ class PriceChannelTest extends Command $this->line('حداقل فاصله ارسال (ثانیه): ' . $info['min_seconds']); $this->line('فاصله cooldown گذشته؟ ' . ($info['cooldown_elapsed'] ? 'بله' : 'خیر (هنوز زوده)')); $this->line('منبع قالب: ' . ($info['template_source'] === 'db' ? 'تنظیمات ادمین' : 'پیش‌فرض داخلی')); + if (! empty($info['live_trade_buy_rial'])) { + $this->line('قیمت فروش اپ (buy API): ' . number_format((int) $info['live_trade_buy_rial']) . ' ریال'); + $this->line('قیمت خرید اپ (sell API): ' . number_format((int) $info['live_trade_sell_rial']) . ' ریال'); + $this->line('فروش گرم کانال: ' . ($info['channel_gram_sell'] ?: '—')); + $this->line('خرید گرم کانال: ' . ($info['channel_gram_buy'] ?: '—')); + } $this->line('تعداد محصولات واجد شرایط: ' . $info['eligible_products']); $this->line('مقصدهای فعال (آماده ارسال): ' . (implode(', ', $info['active_targets']) ?: '— (هیچ)')); diff --git a/app/Services/PriceChannelBroadcastService.php b/app/Services/PriceChannelBroadcastService.php index d3b5e9a..09b82fb 100644 --- a/app/Services/PriceChannelBroadcastService.php +++ b/app/Services/PriceChannelBroadcastService.php @@ -15,7 +15,7 @@ use Illuminate\Support\Facades\Log; * اصول (طبق rule ساعت کاری): * • هیچ تنظیمی را بازنویسی نمی‌کند — فقط می‌خواند. * • فقط پیام‌رسان‌هایی که ادمین انتخاب کرده و توکن + مقصد دارند ارسال می‌شود. - * • قیمت هر محصول از قیمت پایه ذخیره‌شده + کارمزد نمایش همان اپ ساخته می‌شود. + * • قیمت تابلو همان buy/sell کش‌شدهٔ /api/price/last است (مثل صفحهٔ اصلی اپ). */ class PriceChannelBroadcastService { @@ -84,13 +84,19 @@ TXT; $products = $this->eligibleProducts(); $boardProduct = $this->resolveBoardProduct($products); - if ($boardProduct === null || (int) ($boardProduct->price ?? 0) <= 0) { + if ($boardProduct === null) { Log::debug('PriceChannel: محصول تابلو (آبشده) واجد شرایطی نیست'); return; } + $trade = $this->resolveLiveTradePrices(); + if ($trade === null || $trade['buy'] <= 0) { + Log::debug('PriceChannel: مظنه زنده تابلو در دسترس نیست'); + return; + } + // شرط ۲: قیمت تابلو یا قالب پیام نسبت به آخرین ارسال واقعاً تغییر کرده باشد - $signature = $this->broadcastSignature($boardProduct); + $signature = $this->broadcastSignature(); if ($signature === Cache::get(self::SIGNATURE_CACHE_KEY)) { return; } @@ -125,9 +131,9 @@ TXT; /** * امضای ارسال — قیمت تابلو + قالب پیام؛ تغییر هر کدام باید ارسال بعدی را مجاز کند. */ - private function broadcastSignature(Product $boardProduct): string + private function broadcastSignature(): string { - $prices = $this->computeDisplayPrices($boardProduct); + $prices = $this->computeDisplayPrices(); return md5(implode('|', [ $prices['gram_buy'], @@ -161,7 +167,12 @@ TXT; try { $products = $this->eligibleProducts(); $boardProduct = $this->resolveBoardProduct($products); - if ($boardProduct === null || (int) ($boardProduct->price ?? 0) <= 0) { + if ($boardProduct === null) { + return; + } + + $trade = $this->resolveLiveTradePrices(); + if ($trade === null || $trade['buy'] <= 0) { return; } @@ -175,7 +186,7 @@ TXT; $results[$messenger] = $this->dispatchToMessenger($messenger, $target['token'], $target['chat_ids'], $message); } - $signature = $this->broadcastSignature($boardProduct); + $signature = $this->broadcastSignature(); $this->markSent(); Cache::put(self::SIGNATURE_CACHE_KEY, $signature, now()->addDay()); @@ -211,10 +222,8 @@ TXT; if ($message !== '' && $targets !== []) { $this->markSent(); - $products = $this->eligibleProducts(); - $boardProduct = $this->resolveBoardProduct($products); - if ($boardProduct !== null) { - Cache::put(self::SIGNATURE_CACHE_KEY, $this->broadcastSignature($boardProduct), now()->addDay()); + if ($this->resolveLiveTradePrices() !== null) { + Cache::put(self::SIGNATURE_CACHE_KEY, $this->broadcastSignature(), now()->addDay()); } } @@ -249,6 +258,8 @@ TXT; $message = $this->buildMessage(); $fromConfig = trim((string) $this->config->getString('price_channel_message_text', '')); + $trade = $this->resolveLiveTradePrices(); + $display = $trade !== null ? $this->computeDisplayPrices() : null; return [ 'enabled' => $this->config->getBool('price_channel_enabled', false), @@ -259,6 +270,10 @@ TXT; 'messengers' => $messengers, 'active_targets' => array_keys($this->resolveActiveTargets()), 'template_source' => $fromConfig !== '' ? 'db' : 'default', + 'live_trade_buy_rial' => $trade['buy'] ?? 0, + 'live_trade_sell_rial' => $trade['sell'] ?? 0, + 'channel_gram_sell' => $display['gram_sell'] ?? '', + 'channel_gram_buy' => $display['gram_buy'] ?? '', 'message_preview' => $message, ]; } @@ -323,7 +338,12 @@ TXT; { $products = $products ?? $this->eligibleProducts(); $boardProduct = $boardProduct ?? $this->resolveBoardProduct($products); - if ($boardProduct === null || (int) ($boardProduct->price ?? 0) <= 0) { + if ($boardProduct === null) { + return ''; + } + + $trade = $this->resolveLiveTradePrices(); + if ($trade === null || $trade['buy'] <= 0) { return ''; } @@ -349,7 +369,7 @@ TXT; { $name = trim((string) $this->config->getString('name_fa', '')); $title = $name !== '' ? "📊 قیمت لحظه‌ای {$name}" : '📊 قیمت لحظه‌ای'; - $prices = $this->computeDisplayPrices($boardProduct); + $prices = $this->computeDisplayPrices(); $message = $this->applyTemplatePlaceholders($template, [ 'مثقال_فروش' => $prices['mesghal_sell'], @@ -416,21 +436,78 @@ TXT; } /** - * قیمت‌های نمایشی تابلو (گرم + مثقال) — همان منطق اپ + price_channel_diff. + * همان buy/sell اپ — کش live_price_api_payload پرشده از /api/price/last. + * + * @return array{buy: int, sell: int}|null + */ + private function resolveLiveTradePrices(): ?array + { + $display = app(ProductDisplayPriceService::class); + $display->warmLivePriceCache(); + + $cached = Cache::get(ProductDisplayPriceService::LIVE_PRICE_CACHE_KEY); + if (is_array($cached)) { + $buy = (int) ($cached['buy'] ?? 0); + $sell = (int) ($cached['sell'] ?? 0); + if ($buy <= 0) { + $buy = (int) ($cached['price_last'] ?? 0); + } + if ($buy > 0 && $sell > 0) { + return ['buy' => $buy, 'sell' => $sell]; + } + } + + $base = $display->resolveAppBasePrice(); + if ($base <= 0) { + return null; + } + + $provider = $this->config->priceProvider(); + $priceSource = in_array($provider, ['montzar', 'zaarbox', 'main_product'], true) ? $provider : null; + $flags = $provider === 'montzar' ? $this->config->montzarOpenFlags() : []; + + $trade = $this->config->buildTradeDisplayPrices( + (float) $base, + $priceSource, + [ + 'montzar_open_buy' => $flags['open_buy'] ?? true, + 'montzar_open_sell' => $flags['open_sell'] ?? true, + ] + ); + + if ($trade['buy'] <= 0 && $trade['sell'] <= 0) { + return null; + } + + return [ + 'buy' => (int) $trade['buy'], + 'sell' => (int) $trade['sell'], + ]; + } + + /** + * قیمت‌های نمایشی تابلو — همان منبع صفحهٔ اصلی اپ + price_channel_diff. * * @return array{gram_buy: string, gram_sell: string, mesghal_buy: string, mesghal_sell: string} */ - private function computeDisplayPrices(Product $product): array + private function computeDisplayPrices(): array { $channelDiff = (float) $this->config->getInt('price_channel_diff', 0); - $diffBuy = (float) $this->config->displayBuyPriceDiffRial(); - $diffSell = (float) $this->config->displaySellPriceDiffRial(); $mesghal = $this->changeMesghal(); - $base = (float) ($product->price ?? 0); + $trade = $this->resolveLiveTradePrices(); - // 🔴 خرید (مشتری می‌فروشد، پایین‌تر) — 🟢 فروش (مشتری می‌خرد، بالاتر) - $gramBuy = $base - $diffSell - $channelDiff; - $gramSell = $base + $diffBuy + $channelDiff; + if ($trade === null) { + return [ + 'gram_buy' => '', + 'gram_sell' => '', + 'mesghal_buy' => '', + 'mesghal_sell' => '', + ]; + } + + // sell در API = خرید مشتری (پایین) — buy در API = فروش مشتری (بالا) — مثل CurrentPrice.vue + $gramBuy = (float) $trade['sell'] - $channelDiff; + $gramSell = (float) $trade['buy'] + $channelDiff; return [ 'gram_buy' => $this->formatPrice($gramBuy), diff --git a/database/sql/deploy_settings_const.sql b/database/sql/deploy_settings_const.sql index 24a8793..967cabd 100644 --- a/database/sql/deploy_settings_const.sql +++ b/database/sql/deploy_settings_const.sql @@ -40,7 +40,7 @@ SET @invite_link_default_text = CONCAT( SET @invite_link_desc = 'متن پیام اشتراک‌گذاری دعوت در اپ کاربر (دکمه «اشتراک با دوستان» در صفحه اصلی، بخش معرف در پروفایل، صفحه کسب درآمد و مودال اشتراک). متغیرهای جایگزین: ٪نام یا %نام = نام کامل کاربر دعوت‌کننده؛ ٪لینک یا %لینک = آدرس کامل صفحه ثبت‌نام همراه با کد معرف (بر اساس تنظیم app_url یا دامنه فعلی مرورگر). فاصله اختیاری بعد از ٪ یا % مجاز است (مثلاً ٪ نام). اگر این فیلد خالی باشد، اپ از متن پیش‌فرض داخلی استفاده می‌کند.'; SET @deposit_guide_desc = 'متن راهنمای واریز در مودال شارژ حساب. متغیرها: ٪نام یا %نام = نام صاحب حساب؛ ٪کارت یا %کارت = شماره کارت؛ ٪شبا یا %شبا = شبا. فاصله بعد از ٪ اختیاری است. اگر خالی باشد از متن پیش‌فرض اپ استفاده می‌شود.'; -SET @deposit_support_msg_desc = 'متن پیام هنگام کلیک روی «پیام به پشتیبانی» و «پیام به پشتیبانی (sms)» در مودال واریز. همان متغیرهای ٪نام، ٪کارت و ٪شبا. پیش‌فرض: «شماره حساب».'; +SET @deposit_support_msg_desc = 'متن پیام هنگام کلیک روی «پیام به پشتیبانی» و «پیام به پشتیبانی (sms)» در مودال واریز. فقط متغیر ٪نام یا %نام = نام صاحب حساب (deposit_account_holder). پیش‌فرض: «شماره حساب».'; SET @price_channel_message_desc = 'قالب متن پیام ارسالی به کانال. متغیرهای قیمت تابلو: ٪گرم_خرید ٪گرم_فروش ٪مثقال_خرید ٪مثقال_فروش (یا با %). زمان: ٪تاریخ ٪ساعت. اپ: ٪اسم_اپ یا ٪نام (نام فارسی)؛ ٪عنوان = خط «📊 قیمت لحظه‌ای + نام». تماس جدا: ٪آیدی ٪شماره ٪لینک. اختیاری: ٪محصولات (لیست همه محصولات)؛ ٪فوتر (ترکیب آیدی+شماره+لینک). فاصله بعد از ٪ اختیاری است. خالی = قالب پیش‌فرض.'; SET @price_channel_default_text = CONCAT( diff --git a/database/sql/deploy_settings_const_preserve_value_sync_can_edit.sql b/database/sql/deploy_settings_const_preserve_value_sync_can_edit.sql index f80696d..df3e2ab 100644 --- a/database/sql/deploy_settings_const_preserve_value_sync_can_edit.sql +++ b/database/sql/deploy_settings_const_preserve_value_sync_can_edit.sql @@ -39,7 +39,7 @@ SET @invite_link_default_text = CONCAT( SET @invite_link_desc = 'متن پیام اشتراک‌گذاری دعوت در اپ کاربر (دکمه «اشتراک با دوستان» در صفحه اصلی، بخش معرف در پروفایل، صفحه کسب درآمد و مودال اشتراک). متغیرهای جایگزین: ٪نام یا %نام = نام کامل کاربر دعوت‌کننده؛ ٪لینک یا %لینک = آدرس کامل صفحه ثبت‌نام همراه با کد معرف (بر اساس تنظیم app_url یا دامنه فعلی مرورگر). فاصله اختیاری بعد از ٪ یا % مجاز است (مثلاً ٪ نام). اگر این فیلد خالی باشد، اپ از متن پیش‌فرض داخلی استفاده می‌کند.'; SET @deposit_guide_desc = 'متن راهنمای واریز در مودال شارژ حساب. متغیرها: ٪نام یا %نام = نام صاحب حساب؛ ٪کارت یا %کارت = شماره کارت؛ ٪شبا یا %شبا = شبا. فاصله بعد از ٪ اختیاری است. اگر خالی باشد از متن پیش‌فرض اپ استفاده می‌شود.'; -SET @deposit_support_msg_desc = 'متن پیام هنگام کلیک روی «پیام به پشتیبانی» و «پیام به پشتیبانی (sms)» در مودال واریز. همان متغیرهای ٪نام، ٪کارت و ٪شبا. پیش‌فرض: «شماره حساب».'; +SET @deposit_support_msg_desc = 'متن پیام هنگام کلیک روی «پیام به پشتیبانی» و «پیام به پشتیبانی (sms)» در مودال واریز. فقط متغیر ٪نام یا %نام = نام صاحب حساب (deposit_account_holder). پیش‌فرض: «شماره حساب».'; SET @price_channel_message_desc = 'قالب متن پیام ارسالی به کانال. متغیرهای قیمت تابلو: ٪گرم_خرید ٪گرم_فروش ٪مثقال_خرید ٪مثقال_فروش (یا با %). زمان: ٪تاریخ ٪ساعت. اپ: ٪اسم_اپ یا ٪نام (نام فارسی)؛ ٪عنوان = خط «📊 قیمت لحظه‌ای + نام». تماس جدا: ٪آیدی ٪شماره ٪لینک. اختیاری: ٪محصولات (لیست همه محصولات)؛ ٪فوتر (ترکیب آیدی+شماره+لینک). فاصله بعد از ٪ اختیاری است. خالی = قالب پیش‌فرض.'; SET @price_channel_default_text = CONCAT( diff --git a/database/sql/deposit_guide_settings.sql b/database/sql/deposit_guide_settings.sql index 11d023a..c945fd4 100644 --- a/database/sql/deposit_guide_settings.sql +++ b/database/sql/deposit_guide_settings.sql @@ -6,7 +6,7 @@ START TRANSACTION; SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci; SET @deposit_guide_desc = 'متن راهنمای واریز در مودال شارژ حساب. متغیرها: ٪نام یا %نام = نام صاحب حساب؛ ٪کارت یا %کارت = شماره کارت؛ ٪شبا یا %شبا = شبا. فاصله بعد از ٪ اختیاری است. اگر خالی باشد از متن پیش‌فرض اپ استفاده می‌شود.'; -SET @deposit_support_msg_desc = 'متن پیام هنگام کلیک روی «پیام به پشتیبانی» و «پیام به پشتیبانی (sms)» در مودال واریز. همان متغیرهای ٪نام، ٪کارت و ٪شبا. پیش‌فرض: «شماره حساب».'; +SET @deposit_support_msg_desc = 'متن پیام هنگام کلیک روی «پیام به پشتیبانی» و «پیام به پشتیبانی (sms)» در مودال واریز. فقط متغیر ٪نام یا %نام = نام صاحب حساب (deposit_account_holder). پیش‌فرض: «شماره حساب».'; INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`) SELECT 'deposit_account_holder', '', 'نام صاحب حساب واریز', 1, 1,