fix some handling 3

This commit is contained in:
2026-07-08 12:45:14 +03:30
parent 5781d5bf53
commit a4e98d8047
6 changed files with 311 additions and 61 deletions
@@ -611,29 +611,32 @@ class ProductController extends SuperController
if (!Product::hasOrderLimitColumns()) { if (!Product::hasOrderLimitColumns()) {
unset($data['order_limit_enabled'], $data['min_order_cash'], $data['max_order_cash']); unset($data['order_limit_enabled'], $data['min_order_cash'], $data['max_order_cash']);
} elseif (empty($data['order_limit_enabled'])) { } else {
$min = $this->normalizeOrderLimitCash($data['min_order_cash'] ?? null);
$max = $this->normalizeOrderLimitCash($data['max_order_cash'] ?? null);
$orderLimitEnabled = filter_var($data['order_limit_enabled'] ?? false, FILTER_VALIDATE_BOOLEAN)
|| $min !== null
|| $max !== null;
if (!$orderLimitEnabled) {
$data['order_limit_enabled'] = false; $data['order_limit_enabled'] = false;
$data['min_order_cash'] = null; $data['min_order_cash'] = null;
$data['max_order_cash'] = null; $data['max_order_cash'] = null;
} else { } else {
$min = $data['min_order_cash'] ?? null; if ($min === null && $max === null) {
$max = $data['max_order_cash'] ?? null;
$hasMin = $min !== null && $min !== '';
$hasMax = $max !== null && $max !== '';
if (!$hasMin && !$hasMax) {
throw new UnprocessableEntityHttpException( throw new UnprocessableEntityHttpException(
'با فعال بودن محدودیت مبلغ سفارش، حداقل یکی از حداقل یا حداکثر مبلغ باید وارد شود' 'با فعال بودن محدودیت مبلغ سفارش، حداقل یکی از حداقل یا حداکثر مبلغ باید وارد شود'
); );
} }
if ($hasMin && $hasMax && (float) $min > (float) $max) { if ($min !== null && $max !== null && $min > $max) {
throw new UnprocessableEntityHttpException('حداقل مبلغ نمی‌تواند بیشتر از حداکثر باشد'); throw new UnprocessableEntityHttpException('حداقل مبلغ نمی‌تواند بیشتر از حداکثر باشد');
} }
$data['order_limit_enabled'] = true; $data['order_limit_enabled'] = true;
$data['min_order_cash'] = $hasMin ? (int) $min : null; $data['min_order_cash'] = $min;
$data['max_order_cash'] = $hasMax ? (int) $max : null; $data['max_order_cash'] = $max;
}
} }
if (!Product::hasShowInAssetsColumn()) { if (!Product::hasShowInAssetsColumn()) {
@@ -704,4 +707,39 @@ class ProductController extends SuperController
return (float) $value; return (float) $value;
} }
private function normalizeOrderLimitCash(mixed $value): ?int
{
if ($value === null || $value === '') {
return null;
}
if (is_int($value)) {
return $value >= 0 ? $value : null;
}
if (is_float($value)) {
return $value >= 0 ? (int) $value : null;
}
if (! is_string($value)) {
return null;
}
$value = $this->normalizeAsciiDigits(trim(str_replace([' ', ',', '٬'], '', $value)));
if ($value === '' || ! ctype_digit($value)) {
return null;
}
return (int) $value;
}
private function normalizeAsciiDigits(string $value): string
{
$persian = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
$arabic = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'];
$latin = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
return str_replace(array_merge($persian, $arabic), array_merge($latin, $latin), $value);
}
} }
+4 -4
View File
@@ -146,10 +146,10 @@ class Product extends Model
} }
try { try {
static::$orderLimitColumnsExist = Schema::hasColumn( $table = (new static)->getTable();
(new static)->getTable(), static::$orderLimitColumnsExist = Schema::hasColumn($table, 'order_limit_enabled')
'order_limit_enabled' && Schema::hasColumn($table, 'min_order_cash')
); && Schema::hasColumn($table, 'max_order_cash');
} catch (\Throwable) { } catch (\Throwable) {
static::$orderLimitColumnsExist = false; static::$orderLimitColumnsExist = false;
} }
+162 -33
View File
@@ -4,6 +4,7 @@ namespace App\Services;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Product; use App\Models\Product;
use Hekmatinasser\Verta\Verta;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@@ -29,6 +30,22 @@ class PriceChannelBroadcastService
/** وزن هر مثقال به گرم — ضریب تبدیل قیمت هر گرم به هر مثقال (مثقال = گرم × این عدد) */ /** وزن هر مثقال به گرم — ضریب تبدیل قیمت هر گرم به هر مثقال (مثقال = گرم × این عدد) */
private const DEFAULT_CHANGE_MESGHAL = 4.3318; private const DEFAULT_CHANGE_MESGHAL = 4.3318;
/** قالب پیش‌فرض پیام کانال — وقتی price_channel_message_text خالی باشد */
private const DEFAULT_MESSAGE_TEMPLATE = <<<'TXT'
سلام 👋
%عنوان
📅 %تاریخ 🕐 %ساعت
🔴 خرید گرم: %گرم_خرید
🟢 فروش گرم: %گرم_فروش
🔴 خرید مثقال: %مثقال_خرید
🟢 فروش مثقال: %مثقال_فروش
📞 %شماره
%آیدی
%لینک
TXT;
public function __construct(private readonly ConfigService $config) public function __construct(private readonly ConfigService $config)
{ {
} }
@@ -66,19 +83,19 @@ class PriceChannelBroadcastService
} }
$products = $this->eligibleProducts(); $products = $this->eligibleProducts();
if ($products === []) { $boardProduct = $this->resolveBoardProduct($products);
Log::debug('PriceChannel: محصول واجد شرایطی نیست (active/قیمت/ساعت کاری)'); if ($boardProduct === null || (int) ($boardProduct->price ?? 0) <= 0) {
Log::debug('PriceChannel: محصول تابلو (آبشده) واجد شرایطی نیست');
return; return;
} }
// شرط ۲: قیمتِ نمایشی نسبت به آخرین ارسال واقعاً تغییر کرده باشد؛ وگرنه چیزی نفرست // شرط ۲: قیمت تابلو نسبت به آخرین ارسال واقعاً تغییر کرده باشد
// و تایمر را هم دست نزن (تا سرِ اولین تغییرِ بعدی بلافاصله بفرستد). $signature = $this->priceSignature($boardProduct);
$signature = $this->priceSignature($products);
if ($signature === Cache::get(self::SIGNATURE_CACHE_KEY)) { if ($signature === Cache::get(self::SIGNATURE_CACHE_KEY)) {
return; return;
} }
$message = $this->buildMessage($products); $message = $this->buildMessage($products, $boardProduct);
if ($message === '') { if ($message === '') {
return; return;
} }
@@ -111,9 +128,16 @@ class PriceChannelBroadcastService
* *
* @param array<int, Product> $products * @param array<int, Product> $products
*/ */
private function priceSignature(array $products): string private function priceSignature(Product $boardProduct): string
{ {
return md5($this->buildPriceBody($products)); $prices = $this->computeDisplayPrices($boardProduct);
return md5(implode('|', [
$prices['gram_buy'],
$prices['gram_sell'],
$prices['mesghal_buy'],
$prices['mesghal_sell'],
]));
} }
/** /**
@@ -137,8 +161,9 @@ class PriceChannelBroadcastService
if ($message !== '' && $targets !== []) { if ($message !== '' && $targets !== []) {
$this->markSent(); $this->markSent();
$products = $this->eligibleProducts(); $products = $this->eligibleProducts();
if ($products !== []) { $boardProduct = $this->resolveBoardProduct($products);
Cache::put(self::SIGNATURE_CACHE_KEY, $this->priceSignature($products), now()->addDay()); if ($boardProduct !== null) {
Cache::put(self::SIGNATURE_CACHE_KEY, $this->priceSignature($boardProduct), now()->addDay());
} }
} }
@@ -239,27 +264,135 @@ class PriceChannelBroadcastService
} }
/** /**
* ساخت متن پیام قیمت — مرتب و بر اساس داده‌ی هر محصول. * ساخت متن پیام قیمت — قالب از price_channel_message_text (یا پیش‌فرض داخلی).
*/ */
public function buildMessage(?array $products = null): string public function buildMessage(?array $products = null, ?Product $boardProduct = null): string
{ {
$products = $products ?? $this->eligibleProducts(); $products = $products ?? $this->eligibleProducts();
$body = $this->buildPriceBody($products); $boardProduct = $boardProduct ?? $this->resolveBoardProduct($products);
if ($body === '') { if ($boardProduct === null || (int) ($boardProduct->price ?? 0) <= 0) {
return ''; return '';
} }
return trim($this->header() . "\n\n" . $body); return $this->renderMessageTemplate($this->messageTemplate(), $products, $boardProduct);
}
private function messageTemplate(): string
{
$fromConfig = trim((string) $this->config->getString('price_channel_message_text', ''));
return $fromConfig !== '' ? $fromConfig : self::DEFAULT_MESSAGE_TEMPLATE;
} }
/** /**
* بدنهٔ قیمت (بدون هدر/ساعت) — همان چیزی که «تغییر قیمت» را می‌سازد. * جایگزینی متغیرهای قالب.
* امضای تشخیص تغییر روی همین بدنه‌ی نمایشیِ رُندشده گرفته می‌شود تا اختلاف چند ریالی * قیمت تابلو: %گرم_خرید %گرم_فروش %مثقال_خرید %مثقال_فروش
* که به یک عدد نمایشیِ یکسان می‌رسد، پیام تکراری نفرستد. * زمان: %تاریخ %ساعت — اپ: %اسم_اپ %نام %عنوان
* تماس: %آیدی %شماره %لینک — اختیاری: %محصولات %فوتر
* *
* @param array<int, Product> $products * @param array<int, Product> $products
*/ */
private function buildPriceBody(array $products): string private function renderMessageTemplate(string $template, array $products, Product $boardProduct): string
{
$name = trim((string) $this->config->getString('name_fa', ''));
$title = $name !== '' ? "📊 قیمت لحظه‌ای {$name}" : '📊 قیمت لحظه‌ای';
$prices = $this->computeDisplayPrices($boardProduct);
$message = $this->applyTemplatePlaceholders($template, [
'مثقال_فروش' => $prices['mesghal_sell'],
'مثقال_خرید' => $prices['mesghal_buy'],
'گرم_فروش' => $prices['gram_sell'],
'گرم_خرید' => $prices['gram_buy'],
'اسم_اپ' => $name,
'محصولات' => $this->buildProductLines($products),
'عنوان' => $title,
'تاریخ' => Verta::now()->format('Y/n/j'),
'ساعت' => now()->format('H:i'),
'آیدی' => trim((string) $this->config->getString('price_channel_footer_id', '')),
'شماره' => trim((string) $this->config->getString('price_channel_footer_phone', '')),
'لینک' => trim((string) $this->config->getString('price_channel_footer_link', '')),
'فوتر' => $this->buildFooterBlock(),
'نام' => $name,
]);
return preg_replace("/\n{3,}/", "\n\n", trim($message)) ?? trim($message);
}
/** @param array<string, string> $replacements */
private function applyTemplatePlaceholders(string $template, array $replacements): string
{
$keys = array_keys($replacements);
usort($keys, static fn (string $a, string $b): int => mb_strlen($b) <=> mb_strlen($a));
$message = $template;
foreach ($keys as $key) {
$message = preg_replace(
'/[%٪]\s*' . preg_quote($key, '/') . '/u',
$replacements[$key],
$message
) ?? $message;
}
return $message;
}
/**
* محصول مرجع قیمت تابلو — اول آبشده (گرم)، بعد اولین محصول گرم، بعد اولین واجد شرایط.
*
* @param array<int, Product> $products
*/
private function resolveBoardProduct(array $products): ?Product
{
if ($products === []) {
return null;
}
foreach ($products as $product) {
if (trim((string) ($product->group_type ?? '')) === 'آبشده' && $this->isGramUnit($product)) {
return $product;
}
}
foreach ($products as $product) {
if ($this->isGramUnit($product)) {
return $product;
}
}
return $products[0];
}
/**
* قیمت‌های نمایشی تابلو (گرم + مثقال) — همان منطق اپ + price_channel_diff.
*
* @return array{gram_buy: string, gram_sell: string, mesghal_buy: string, mesghal_sell: string}
*/
private function computeDisplayPrices(Product $product): 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);
// 🔴 خرید (مشتری می‌فروشد، پایین‌تر) — 🟢 فروش (مشتری می‌خرد، بالاتر)
$gramBuy = $base - $diffSell - $channelDiff;
$gramSell = $base + $diffBuy + $channelDiff;
return [
'gram_buy' => $this->formatPrice($gramBuy),
'gram_sell' => $this->formatPrice($gramSell),
'mesghal_buy' => $mesghal > 0 ? $this->formatPrice($gramBuy * $mesghal) : '',
'mesghal_sell' => $mesghal > 0 ? $this->formatPrice($gramSell * $mesghal) : '',
];
}
/**
* بدنهٔ قیمت محصولات (بدون فوتر) — مبنای تشخیص تغییر قیمت.
*
* @param array<int, Product> $products
*/
private function buildProductLines(array $products): string
{ {
if ($products === []) { if ($products === []) {
return ''; return '';
@@ -283,7 +416,6 @@ class PriceChannelBroadcastService
$lines[] = '🔴 خرید ' . $this->formatPrice($low) . ' 🟢 فروش ' . $this->formatPrice($high); $lines[] = '🔴 خرید ' . $this->formatPrice($low) . ' 🟢 فروش ' . $this->formatPrice($high);
if ($this->isGramUnit($product) && $mesghal > 0) { if ($this->isGramUnit($product) && $mesghal > 0) {
// قیمت پایه «هر گرم» است؛ مثقال = گرم × ضریب (کنوانسیون اپ: مثقال = گرم × 4.3318)
$lines[] = '💰 هر مثقال: ' . $this->formatPrice($low * $mesghal) $lines[] = '💰 هر مثقال: ' . $this->formatPrice($low * $mesghal)
. ' / ' . $this->formatPrice($high * $mesghal); . ' / ' . $this->formatPrice($high * $mesghal);
} }
@@ -291,25 +423,22 @@ class PriceChannelBroadcastService
$lines[] = ''; $lines[] = '';
} }
$footer = $this->footerLines();
if ($footer !== []) {
$lines[] = '➖➖➖➖➖➖';
foreach ($footer as $line) {
$lines[] = $line;
}
}
return trim(implode("\n", $lines)); return trim(implode("\n", $lines));
} }
private function header(): string private function buildFooterBlock(): string
{ {
$name = trim((string) $this->config->getString('name_fa', '')); $footer = $this->footerLines();
$time = now()->format('H:i'); if ($footer === []) {
return '';
}
$title = $name !== '' ? "📊 قیمت لحظه‌ای {$name}" : '📊 قیمت لحظه‌ای'; $lines = ['➖➖➖➖➖➖'];
foreach ($footer as $line) {
$lines[] = $line;
}
return $title . "\n🕐 ساعت " . $time; return implode("\n", $lines);
} }
/** /**
+42 -3
View File
@@ -42,6 +42,22 @@ SET @invite_link_desc = 'متن پیام اشتراک‌گذاری دعوت در
SET @deposit_guide_desc = 'متن راهنمای واریز در مودال شارژ حساب. متغیرها: ٪نام یا %نام = نام صاحب حساب؛ ٪کارت یا %کارت = شماره کارت؛ ٪شبا یا %شبا = شبا. فاصله بعد از ٪ اختیاری است. اگر خالی باشد از متن پیش‌فرض اپ استفاده می‌شود.'; SET @deposit_guide_desc = 'متن راهنمای واریز در مودال شارژ حساب. متغیرها: ٪نام یا %نام = نام صاحب حساب؛ ٪کارت یا %کارت = شماره کارت؛ ٪شبا یا %شبا = شبا. فاصله بعد از ٪ اختیاری است. اگر خالی باشد از متن پیش‌فرض اپ استفاده می‌شود.';
SET @deposit_support_msg_desc = 'متن پیام هنگام کلیک روی «پیام به پشتیبانی» و «پیام به پشتیبانی (sms)» در مودال واریز. همان متغیرهای ٪نام، ٪کارت و ٪شبا. پیش‌فرض: «شماره حساب».'; SET @deposit_support_msg_desc = 'متن پیام هنگام کلیک روی «پیام به پشتیبانی» و «پیام به پشتیبانی (sms)» در مودال واریز. همان متغیرهای ٪نام، ٪کارت و ٪شبا. پیش‌فرض: «شماره حساب».';
SET @price_channel_message_desc = 'قالب متن پیام ارسالی به کانال. متغیرهای قیمت تابلو: ٪گرم_خرید ٪گرم_فروش ٪مثقال_خرید ٪مثقال_فروش (یا با %). زمان: ٪تاریخ ٪ساعت. اپ: ٪اسم_اپ یا ٪نام (نام فارسی)؛ ٪عنوان = خط «📊 قیمت لحظه‌ای + نام». تماس جدا: ٪آیدی ٪شماره ٪لینک. اختیاری: ٪محصولات (لیست همه محصولات)؛ ٪فوتر (ترکیب آیدی+شماره+لینک). فاصله بعد از ٪ اختیاری است. خالی = قالب پیش‌فرض.';
SET @price_channel_default_text = CONCAT(
'سلام 👋', CHAR(10),
'%عنوان', CHAR(10),
'📅 %تاریخ 🕐 %ساعت', CHAR(10),
CHAR(10),
'🔴 خرید گرم: %گرم_خرید', CHAR(10),
'🟢 فروش گرم: %گرم_فروش', CHAR(10),
'🔴 خرید مثقال: %مثقال_خرید', CHAR(10),
'🟢 فروش مثقال: %مثقال_فروش', CHAR(10),
CHAR(10),
'📞 %شماره', CHAR(10),
'%آیدی', CHAR(10),
'%لینک'
);
-- ستون‌های اختیاری settings (بعضی DBهای قدیمی ندارند) -- ستون‌های اختیاری settings (بعضی DBهای قدیمی ندارند)
SET @sql = IF( SET @sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS (SELECT COUNT(*) FROM information_schema.COLUMNS
@@ -379,6 +395,10 @@ SELECT 'price_channel_min_seconds', '120', 'حداقل فاصله ارسال (ث
'حداقل فاصله بین دو ارسال به کانال؛ جلوی سیل پیام را می‌گیرد. صفر = هر تغییر قیمت.', 'number' 'حداقل فاصله بین دو ارسال به کانال؛ جلوی سیل پیام را می‌گیرد. صفر = هر تغییر قیمت.', 'number'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'price_channel_min_seconds' LIMIT 1); WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'price_channel_min_seconds' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'price_channel_message_text', @price_channel_default_text, 'قالب متن پیام کانال', 1, 1, @price_channel_message_desc, 'longtext'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'price_channel_message_text' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`) INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'price_channel_footer_id', '', 'آیدی کانال در پیام', 1, 1, SELECT 'price_channel_footer_id', '', 'آیدی کانال در پیام', 1, 1,
'اگر پر باشد در انتهای پیام نمایش داده می‌شود (مثلاً یوزرنیم کانال). خالی = نمایش داده نمی‌شود.', 'text' 'اگر پر باشد در انتهای پیام نمایش داده می‌شود (مثلاً یوزرنیم کانال). خالی = نمایش داده نمی‌شود.', 'text'
@@ -706,7 +726,7 @@ WHERE `key` = 'main_product';
-- longtext -- longtext
UPDATE `settings` SET `type` = 'longtext' WHERE `key` IN ( UPDATE `settings` SET `type` = 'longtext' WHERE `key` IN (
'text_rule', 'text_about', 'support_messenger_text', 'invite_link_text', 'text_rule', 'text_about', 'support_messenger_text', 'invite_link_text',
'deposit_support_message_text', 'deposit_support_message_text', 'price_channel_message_text',
'montzar_order_response', 'montzar_last_product_log' 'montzar_order_response', 'montzar_last_product_log'
); );
@@ -1127,6 +1147,13 @@ UPDATE `settings` SET `description` = 'نوع پیام‌رسان برای دک
UPDATE `settings` SET `description` = 'شناسه تماس پشتیبانی: یوزرنیم (تلگرام)، شماره (واتساپ/بله) یا آیدی بسته به پیام‌رسان انتخاب‌شده.' WHERE `key` = 'support_messenger_id'; UPDATE `settings` SET `description` = 'شناسه تماس پشتیبانی: یوزرنیم (تلگرام)، شماره (واتساپ/بله) یا آیدی بسته به پیام‌رسان انتخاب‌شده.' WHERE `key` = 'support_messenger_id';
UPDATE `settings` SET `description` = @deposit_guide_desc WHERE `key` = 'support_messenger_text'; UPDATE `settings` SET `description` = @deposit_guide_desc WHERE `key` = 'support_messenger_text';
UPDATE `settings` SET `description` = @deposit_support_msg_desc WHERE `key` = 'deposit_support_message_text'; UPDATE `settings` SET `description` = @deposit_support_msg_desc WHERE `key` = 'deposit_support_message_text';
UPDATE `settings` SET `description` = @price_channel_message_desc WHERE `key` = 'price_channel_message_text';
UPDATE `settings` SET
`title_fa` = 'قالب متن پیام کانال',
`type` = 'longtext',
`can_edit` = 1,
`description` = @price_channel_message_desc
WHERE `key` = 'price_channel_message_text';
UPDATE `settings` SET `description` = 'متن پیام اشتراک‌گذاری دعوت در اپ کاربر (دکمه «اشتراک با دوستان» در صفحه اصلی، بخش معرف در پروفایل، صفحه کسب درآمد و مودال اشتراک). متغیرهای جایگزین: ٪نام یا %نام = نام کامل کاربر دعوت‌کننده؛ ٪لینک یا %لینک = آدرس کامل صفحه ثبت‌نام همراه با کد معرف (بر اساس تنظیم app_url یا دامنه فعلی مرورگر). فاصله اختیاری بعد از ٪ یا % مجاز است (مثلاً ٪ نام). اگر این فیلد خالی باشد، اپ از متن پیش‌فرض داخلی استفاده می‌کند.' WHERE `key` = 'invite_link_text'; UPDATE `settings` SET `description` = 'متن پیام اشتراک‌گذاری دعوت در اپ کاربر (دکمه «اشتراک با دوستان» در صفحه اصلی، بخش معرف در پروفایل، صفحه کسب درآمد و مودال اشتراک). متغیرهای جایگزین: ٪نام یا %نام = نام کامل کاربر دعوت‌کننده؛ ٪لینک یا %لینک = آدرس کامل صفحه ثبت‌نام همراه با کد معرف (بر اساس تنظیم app_url یا دامنه فعلی مرورگر). فاصله اختیاری بعد از ٪ یا % مجاز است (مثلاً ٪ نام). اگر این فیلد خالی باشد، اپ از متن پیش‌فرض داخلی استفاده می‌کند.' WHERE `key` = 'invite_link_text';
UPDATE `settings` SET `description` = 'شماره تماس پشتیبانی؛ در صفحه واریز، درباره ما و قوانین نمایش داده می‌شود. متن پیامک/پشتیبانی از «متن پیام به پشتیبانی (واریز)» خوانده می‌شود.' WHERE `key` = 'deposit_sms_support_number'; UPDATE `settings` SET `description` = 'شماره تماس پشتیبانی؛ در صفحه واریز، درباره ما و قوانین نمایش داده می‌شود. متن پیامک/پشتیبانی از «متن پیام به پشتیبانی (واریز)» خوانده می‌شود.' WHERE `key` = 'deposit_sms_support_number';
@@ -1166,10 +1193,21 @@ UPDATE `settings` SET
`description` = @deposit_support_msg_desc `description` = @deposit_support_msg_desc
WHERE `key` = 'deposit_support_message_text'; WHERE `key` = 'deposit_support_message_text';
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'price_channel_message_text', @price_channel_default_text, 'قالب متن پیام کانال', 1, 1, @price_channel_message_desc, 'longtext'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'price_channel_message_text' LIMIT 1);
UPDATE `settings` SET
`title_fa` = 'قالب متن پیام کانال',
`type` = 'longtext',
`can_edit` = 1,
`description` = @price_channel_message_desc
WHERE `key` = 'price_channel_message_text';
UPDATE `settings` SET `type` = 'longtext' UPDATE `settings` SET `type` = 'longtext'
WHERE `key` IN ( WHERE `key` IN (
'text_rule', 'text_about', 'support_messenger_text', 'invite_link_text', 'text_rule', 'text_about', 'support_messenger_text', 'invite_link_text',
'deposit_support_message_text', 'deposit_support_message_text', 'price_channel_message_text',
'montzar_order_response', 'montzar_last_product_log' 'montzar_order_response', 'montzar_last_product_log'
); );
@@ -1360,7 +1398,8 @@ UPDATE `settings` SET `can_edit` = 1 WHERE `key` IN (
'deposit_support_message_text', 'deposit_account_holder', 'deposit_card_number', 'deposit_sheba', 'deposit_support_message_text', 'deposit_account_holder', 'deposit_card_number', 'deposit_sheba',
'text_about', 'text_rule', 'text_about', 'text_rule',
'price_channel_enabled', 'price_channel_messengers', 'price_channel_diff', 'price_channel_enabled', 'price_channel_messengers', 'price_channel_diff',
'price_channel_min_seconds', 'price_channel_footer_id', 'price_channel_footer_phone', 'price_channel_min_seconds', 'price_channel_message_text',
'price_channel_footer_id', 'price_channel_footer_phone',
'price_channel_footer_link', 'change_mesghal' 'price_channel_footer_link', 'change_mesghal'
); );
@@ -41,6 +41,22 @@ SET @invite_link_desc = 'متن پیام اشتراک‌گذاری دعوت در
SET @deposit_guide_desc = 'متن راهنمای واریز در مودال شارژ حساب. متغیرها: ٪نام یا %نام = نام صاحب حساب؛ ٪کارت یا %کارت = شماره کارت؛ ٪شبا یا %شبا = شبا. فاصله بعد از ٪ اختیاری است. اگر خالی باشد از متن پیش‌فرض اپ استفاده می‌شود.'; SET @deposit_guide_desc = 'متن راهنمای واریز در مودال شارژ حساب. متغیرها: ٪نام یا %نام = نام صاحب حساب؛ ٪کارت یا %کارت = شماره کارت؛ ٪شبا یا %شبا = شبا. فاصله بعد از ٪ اختیاری است. اگر خالی باشد از متن پیش‌فرض اپ استفاده می‌شود.';
SET @deposit_support_msg_desc = 'متن پیام هنگام کلیک روی «پیام به پشتیبانی» و «پیام به پشتیبانی (sms)» در مودال واریز. همان متغیرهای ٪نام، ٪کارت و ٪شبا. پیش‌فرض: «شماره حساب».'; SET @deposit_support_msg_desc = 'متن پیام هنگام کلیک روی «پیام به پشتیبانی» و «پیام به پشتیبانی (sms)» در مودال واریز. همان متغیرهای ٪نام، ٪کارت و ٪شبا. پیش‌فرض: «شماره حساب».';
SET @price_channel_message_desc = 'قالب متن پیام ارسالی به کانال. متغیرهای قیمت تابلو: ٪گرم_خرید ٪گرم_فروش ٪مثقال_خرید ٪مثقال_فروش (یا با %). زمان: ٪تاریخ ٪ساعت. اپ: ٪اسم_اپ یا ٪نام (نام فارسی)؛ ٪عنوان = خط «📊 قیمت لحظه‌ای + نام». تماس جدا: ٪آیدی ٪شماره ٪لینک. اختیاری: ٪محصولات (لیست همه محصولات)؛ ٪فوتر (ترکیب آیدی+شماره+لینک). فاصله بعد از ٪ اختیاری است. خالی = قالب پیش‌فرض.';
SET @price_channel_default_text = CONCAT(
'سلام 👋', CHAR(10),
'%عنوان', CHAR(10),
'📅 %تاریخ 🕐 %ساعت', CHAR(10),
CHAR(10),
'🔴 خرید گرم: %گرم_خرید', CHAR(10),
'🟢 فروش گرم: %گرم_فروش', CHAR(10),
'🔴 خرید مثقال: %مثقال_خرید', CHAR(10),
'🟢 فروش مثقال: %مثقال_فروش', CHAR(10),
CHAR(10),
'📞 %شماره', CHAR(10),
'%آیدی', CHAR(10),
'%لینک'
);
-- ستون‌های اختیاری settings (بعضی DBهای قدیمی ندارند) -- ستون‌های اختیاری settings (بعضی DBهای قدیمی ندارند)
SET @sql = IF( SET @sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS (SELECT COUNT(*) FROM information_schema.COLUMNS
+29 -1
View File
@@ -11,6 +11,22 @@
START TRANSACTION; START TRANSACTION;
SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci; SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci;
SET @price_channel_message_desc = 'قالب متن پیام ارسالی به کانال. متغیرهای قیمت تابلو: ٪گرم_خرید ٪گرم_فروش ٪مثقال_خرید ٪مثقال_فروش (یا با %). زمان: ٪تاریخ ٪ساعت. اپ: ٪اسم_اپ یا ٪نام (نام فارسی)؛ ٪عنوان = خط «📊 قیمت لحظه‌ای + نام». تماس جدا: ٪آیدی ٪شماره ٪لینک. اختیاری: ٪محصولات (لیست همه محصولات)؛ ٪فوتر (ترکیب آیدی+شماره+لینک). فاصله بعد از ٪ اختیاری است. خالی = قالب پیش‌فرض.';
SET @price_channel_default_text = CONCAT(
'سلام 👋', CHAR(10),
'%عنوان', CHAR(10),
'📅 %تاریخ 🕐 %ساعت', CHAR(10),
CHAR(10),
'🔴 خرید گرم: %گرم_خرید', CHAR(10),
'🟢 فروش گرم: %گرم_فروش', CHAR(10),
'🔴 خرید مثقال: %مثقال_خرید', CHAR(10),
'🟢 فروش مثقال: %مثقال_فروش', CHAR(10),
CHAR(10),
'📞 %شماره', CHAR(10),
'%آیدی', CHAR(10),
'%لینک'
);
-- توکن روبیکا (توکن بله/تلگرام از قبل موجودند) — سیستمی (can_edit=0) -- توکن روبیکا (توکن بله/تلگرام از قبل موجودند) — سیستمی (can_edit=0)
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`) INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`)
SELECT 'rubika_bot_token', '', 'توکن بات روبیکا', 0, 1, NULL SELECT 'rubika_bot_token', '', 'توکن بات روبیکا', 0, 1, NULL
@@ -39,6 +55,17 @@ SELECT 'price_channel_min_seconds', '120', 'حداقل فاصله ارسال (ث
'حداقل فاصله بین دو ارسال به کانال؛ جلوی سیل پیام را می‌گیرد. صفر = هر تغییر قیمت.', 'number' 'حداقل فاصله بین دو ارسال به کانال؛ جلوی سیل پیام را می‌گیرد. صفر = هر تغییر قیمت.', 'number'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'price_channel_min_seconds' LIMIT 1); WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'price_channel_min_seconds' LIMIT 1);
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'price_channel_message_text', @price_channel_default_text, 'قالب متن پیام کانال', 1, 1, @price_channel_message_desc, 'longtext'
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'price_channel_message_text' LIMIT 1);
UPDATE `settings` SET
`title_fa` = 'قالب متن پیام کانال',
`type` = 'longtext',
`can_edit` = 1,
`description` = @price_channel_message_desc
WHERE `key` = 'price_channel_message_text';
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`) INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
SELECT 'price_channel_footer_id', '', 'آیدی کانال در پیام', 1, 1, SELECT 'price_channel_footer_id', '', 'آیدی کانال در پیام', 1, 1,
'اگر پر باشد در انتهای پیام نمایش داده می‌شود (مثلاً یوزرنیم کانال). خالی = نمایش داده نمی‌شود.', 'text' 'اگر پر باشد در انتهای پیام نمایش داده می‌شود (مثلاً یوزرنیم کانال). خالی = نمایش داده نمی‌شود.', 'text'
@@ -82,7 +109,8 @@ UPDATE `settings` SET `can_edit` = 0 WHERE `key` IN (
); );
UPDATE `settings` SET `can_edit` = 1 WHERE `key` IN ( UPDATE `settings` SET `can_edit` = 1 WHERE `key` IN (
'price_channel_enabled', 'price_channel_messengers', 'price_channel_diff', 'price_channel_enabled', 'price_channel_messengers', 'price_channel_diff',
'price_channel_min_seconds', 'price_channel_footer_id', 'price_channel_footer_phone', 'price_channel_min_seconds', 'price_channel_message_text',
'price_channel_footer_id', 'price_channel_footer_phone',
'price_channel_footer_link', 'change_mesghal' 'price_channel_footer_link', 'change_mesghal'
); );