From cd81ea8af0990f7549c7df56c14817c041948a28 Mon Sep 17 00:00:00 2001 From: adonis Date: Tue, 30 Jun 2026 21:44:39 +0330 Subject: [PATCH] fix kashkool --- .../Controllers/Admin/ProductController.php | 21 ++++--- app/Http/Controllers/Auth/AuthController.php | 2 +- .../Controllers/Site/ReportController.php | 54 +---------------- app/Http/Middleware/ValidationMiddleware.php | 7 ++- app/Models/Cnt.php | 60 +++++++++++++++++-- app/Services/ConfigService.php | 6 ++ ...0_000001_add_value_json_to_const_table.php | 34 +++++++++++ database/sql/deploy_settings_const.sql | 16 ++--- ...ngs_const_preserve_value_sync_can_edit.sql | 16 ++--- 9 files changed, 130 insertions(+), 86 deletions(-) create mode 100644 database/migrations/2026_06_30_000001_add_value_json_to_const_table.php diff --git a/app/Http/Controllers/Admin/ProductController.php b/app/Http/Controllers/Admin/ProductController.php index 268a67e..23f5927 100644 --- a/app/Http/Controllers/Admin/ProductController.php +++ b/app/Http/Controllers/Admin/ProductController.php @@ -360,11 +360,12 @@ class ProductController extends SuperController public function activationSnapshotStatus() { $row = Cnt::where('key', self::ACTIVATION_SNAPSHOT_KEY)->first(); - $items = $row?->value_json['items'] ?? []; + $payload = $row?->jsonPayload() ?? []; + $items = $payload['items'] ?? []; return response()->json([ 'has_snapshot' => ! empty($items), - 'saved_at' => $row?->value_json['saved_at'] ?? null, + 'saved_at' => $payload['saved_at'] ?? null, 'count' => count($items), ]); } @@ -387,15 +388,13 @@ class ProductController extends SuperController 'open_sell' => (bool) $product->open_sell, ])->values()->all(); - Cnt::updateOrCreate( - ['key' => self::ACTIVATION_SNAPSHOT_KEY], + Cnt::saveJsonPayload( + self::ACTIVATION_SNAPSHOT_KEY, [ - 'title_fa' => 'ذخیره وضعیت محصولات', - 'value_json' => [ - 'saved_at' => now()->timezone('Asia/Tehran')->format('Y-m-d H:i:s'), - 'items' => $items, - ], - ] + 'saved_at' => now()->timezone('Asia/Tehran')->format('Y-m-d H:i:s'), + 'items' => $items, + ], + 'ذخیره وضعیت محصولات' ); $affected = Product::query()->update([ @@ -418,7 +417,7 @@ class ProductController extends SuperController public function restoreActivationSnapshot() { $row = Cnt::where('key', self::ACTIVATION_SNAPSHOT_KEY)->first(); - $items = $row?->value_json['items'] ?? []; + $items = $row?->jsonPayload()['items'] ?? []; if (empty($items)) { return response()->json(['error' => 'اسنAPSHOTی برای بازگردانی وجود ندارد.'], 422); diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php index 4c5720b..e0f434f 100644 --- a/app/Http/Controllers/Auth/AuthController.php +++ b/app/Http/Controllers/Auth/AuthController.php @@ -164,7 +164,7 @@ class AuthController extends Controller return response()->json(['message' => 'کد نامعتبر می باشد'], 422); } } else { - if ($this->Config('referral_id') == 'required') { + if ($cfg->isReferralRequired()) { return response()->json(['message' => 'کد معرف اجباریست'], 422); } } diff --git a/app/Http/Controllers/Site/ReportController.php b/app/Http/Controllers/Site/ReportController.php index 39a3a01..e35a80a 100644 --- a/app/Http/Controllers/Site/ReportController.php +++ b/app/Http/Controllers/Site/ReportController.php @@ -494,15 +494,13 @@ class ReportController extends Controller } /** - * فروشگاه — منطق lastPrice قبل مرج (ghatreh): montzar_price / nasiryan / prices + * فروشگاه — منطق lastPrice: montzar_price / prices */ protected function storeLastPrice(ConfigService $config): array { $toBool = fn ($value) => filter_var($value, FILTER_VALIDATE_BOOLEAN); - $difference = (float) config('fee.single'); $talaIr = 0; - $nasiryan = 0; $montzar = 0; $montzarExists = Setting::where('key', 'montzar_price')->exists(); @@ -560,58 +558,9 @@ class ReportController extends Controller } } - $nasiryanEnabled = $config->Config('price_from_nasiryan') == 'true'; - if ($nasiryanEnabled) { - try { - $nasiryanRes = Http::withoutVerifying() - ->timeout(2) - ->withToken(Cnt::where('key', 'nasiryan')->first()['value'] ?? '') - ->get('https://nasiryan.com/server/customer/product/list') - ->json(); - - if ((isset($nasiryanRes['message']) && $nasiryanRes['message'] == 'no authorised') || $nasiryanRes == null) { - $cnt = new Cnt(); - $cnt->nasiryan_login(); - $nasiryanRes = Http::withoutVerifying() - ->timeout(2) - ->withToken(Cnt::where('key', 'nasiryan')->first()['value'] ?? '') - ->get('https://nasiryan.com/server/customer/product/list') - ->json(); - } - - $lp = collect($nasiryanRes)->firstWhere('id', 1); - if (! $lp || ! isset($lp['sub_agent'])) { - throw new \Exception('Nasiryan product/sub_agent not found'); - } - - $buy = (float) $lp['sub_agent']['rate_buy_gram_price']; - $sell = (float) $lp['sub_agent']['rate_sell_gram_price']; - $customPrice = [ - 'price' => ($buy + $sell) / 2, - 'diff' => ($buy - $sell) / 2, - ]; - - Cnt::where('key', 'nasiryan_price')->update(['value' => (float) $customPrice['price']]); - Cnt::where('key', 'nasiryan_diff')->update(['value' => (float) $customPrice['diff']]); - Cnt::where('key', 'nasiryan_mazaneh')->update(['value' => ((float) $customPrice['price'] * 4.331802)]); - Cnt::where('key', 'nasiryan_truly_buy_mazaneh')->update(['value' => $lp['sub_agent']['rate_buy_price']]); - Cnt::where('key', 'nasiryan_truly_sell_mazaneh')->update(['value' => $lp['sub_agent']['rate_sell_price']]); - Cnt::where('key', 'log')->update(['value' => json_encode($nasiryanRes, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)]); - - $nasiryan = (int) $customPrice['price']; - Cnt::where('key', 'nasiryan_closed')->update(['value' => $nasiryan == 0 ? 'true' : 'false']); - } catch (\Throwable $e) { - $nasiryan = 0; - Cnt::where('key', 'nasiryan_closed')->update(['value' => 'true']); - } - } - if ($montzarEnabled && $montzar !== 0) { $priceValue = $montzar; $priceSource = 'montzar'; - } elseif ($nasiryanEnabled && $nasiryan !== 0) { - $priceValue = $nasiryan; - $priceSource = 'nasiryan'; } else { $priceValue = $talaIr; $priceSource = 'tala_ir'; @@ -637,7 +586,6 @@ class ReportController extends Controller 'price_last' => $display['priceLast'], 'price' => $priceValue, 'tala_ir' => $talaIr, - 'nasiryan' => $nasiryan, ]; if ($montzarExists) { diff --git a/app/Http/Middleware/ValidationMiddleware.php b/app/Http/Middleware/ValidationMiddleware.php index d26ec91..cb4f140 100644 --- a/app/Http/Middleware/ValidationMiddleware.php +++ b/app/Http/Middleware/ValidationMiddleware.php @@ -54,8 +54,13 @@ class ValidationMiddleware private function fa2en_numbers($input) { if (is_string($input)) { $fa_numbers = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹']; + $ar_numbers = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩']; $en_numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; - return str_replace($fa_numbers, $en_numbers, $input); + $input = str_replace($fa_numbers, $en_numbers, $input); + $input = str_replace($ar_numbers, $en_numbers, $input); + $input = str_replace('٫', '.', $input); + + return $input; } elseif (is_array($input)) { return array_map([$this, __FUNCTION__], $input); } else { diff --git a/app/Models/Cnt.php b/app/Models/Cnt.php index c915cd2..6ae270b 100644 --- a/app/Models/Cnt.php +++ b/app/Models/Cnt.php @@ -2,22 +2,74 @@ namespace App\Models; -use App\Http\Controllers\Controller; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; -use Illuminate\Support\Facades\Http; +use Illuminate\Support\Facades\Schema; class Cnt extends Model { use HasFactory; + + private static ?bool $valueJsonColumnExists = null; + protected $table = 'const'; + protected $fillable = [ 'key', 'title_fa', 'value', - 'value_json' + 'value_json', ]; + protected $casts = [ - 'value_json'=>'array' + 'value_json' => 'array', ]; + + public static function hasValueJsonColumn(): bool + { + if (static::$valueJsonColumnExists !== null) { + return static::$valueJsonColumnExists; + } + + try { + static::$valueJsonColumnExists = Schema::hasColumn((new static)->getTable(), 'value_json'); + } catch (\Throwable) { + static::$valueJsonColumnExists = false; + } + + return static::$valueJsonColumnExists; + } + + public function jsonPayload(): array + { + if (static::hasValueJsonColumn() && is_array($this->value_json)) { + return $this->value_json; + } + + $raw = $this->value ?? ''; + if ($raw === '' || $raw === null) { + return []; + } + + $decoded = json_decode((string) $raw, true); + + return is_array($decoded) ? $decoded : []; + } + + public static function saveJsonPayload(string $key, array $payload, ?string $titleFa = null): self + { + $data = []; + + if ($titleFa !== null) { + $data['title_fa'] = $titleFa; + } + + if (static::hasValueJsonColumn()) { + $data['value_json'] = $payload; + } else { + $data['value'] = json_encode($payload, JSON_UNESCAPED_UNICODE); + } + + return static::updateOrCreate(['key' => $key], $data); + } } diff --git a/app/Services/ConfigService.php b/app/Services/ConfigService.php index 9f92196..5e8c980 100644 --- a/app/Services/ConfigService.php +++ b/app/Services/ConfigService.php @@ -63,6 +63,12 @@ class ConfigService return $this->getBool('is_store', false); } + /** کد معرف در ثبت‌نام اجباری است (تنظیم referral_id = required) */ + public function isReferralRequired(): bool + { + return strtolower(trim($this->getString('referral_id'))) === 'required'; + } + public function getInt(string $key, int $default = 0): int { $value = $this->get($key, $default); diff --git a/database/migrations/2026_06_30_000001_add_value_json_to_const_table.php b/database/migrations/2026_06_30_000001_add_value_json_to_const_table.php new file mode 100644 index 0000000..5f5a8e0 --- /dev/null +++ b/database/migrations/2026_06_30_000001_add_value_json_to_const_table.php @@ -0,0 +1,34 @@ +json('value_json')->nullable()->after('value'); + } else { + $table->json('value_json')->nullable(); + } + }); + } + + public function down(): void + { + if (! Schema::hasTable('const') || ! Schema::hasColumn('const', 'value_json')) { + return; + } + + Schema::table('const', function (Blueprint $table) { + $table->dropColumn('value_json'); + }); + } +}; diff --git a/database/sql/deploy_settings_const.sql b/database/sql/deploy_settings_const.sql index 07941a1..5a6d788 100644 --- a/database/sql/deploy_settings_const.sql +++ b/database/sql/deploy_settings_const.sql @@ -942,14 +942,14 @@ UPDATE `settings` SET `description` = 'شناسه عددی یا نام reading_m UPDATE `settings` SET `description` = 'آدرس پایه API سرویس تابلو آنلاین (بدون مسیر انتهایی). مثال: https://example.com/api' WHERE `key` = 'zaarbox_api_url'; UPDATE `settings` SET `description` = 'کلید یا UUID ردیف قیمت در پاسخ API تابلو. باید با محصول/ردیف موردنظر در سرویس خارجی یکسان باشد.' WHERE `key` = 'zaarbox_price_key'; UPDATE `settings` SET `description` = 'روشن: اگر API قیمت در دسترس نباشد، آخرین قیمت ذخیره‌شده در دیتابیس به کاربر نشان داده می‌شود. خاموش: در قطعی API ممکن است قیمت صفر نمایش داده شود.' WHERE `key` = 'show_last_price'; -UPDATE `settings` SET `description` = 'روشن: خارج از بازه startDay تا endDay معامله و نمایش قیمت بسته می‌شود. خاموش: محدودیت ساعتی اعمال نمی‌شود.' WHERE `key` = 'close_price'; -UPDATE `settings` SET `description` = 'ساعت شروع بازه معامله روزانه (فرمت 24 ساعته، مثال 09:00). فقط وقتی close_price روشن باشد اثر دارد.' WHERE `key` = 'startDay'; -UPDATE `settings` SET `description` = 'ساعت پایان بازه معامله روزانه (فرمت 24 ساعته، مثال 22:00). فقط وقتی close_price روشن باشد اثر دارد.' WHERE `key` = 'endDay'; -UPDATE `settings` SET `description` = 'اختلاف پایه (ریال) برای محاسبه قیمت خرید و فروش نمایشی روی مظنه. معمولاً نصف فاصله اسپرد کل است.' WHERE `key` = 'fee_single'; -UPDATE `settings` SET `description` = 'مبلغ اضافه (ریال) روی قیمت پایه برای نمایش/محاسبه سمت خرید. خالی = استفاده از fee_single.' WHERE `key` = 'buy_fee'; -UPDATE `settings` SET `description` = 'مبلغ کسر (ریال) از قیمت پایه برای نمایش/محاسبه سمت فروش. خالی = استفاده از fee_single.' WHERE `key` = 'sell_fee'; -UPDATE `settings` SET `description` = 'کارمزد ثابت ریالی هر سفارش (legacy). در صورت تنظیم fee_service_charge_percentage معمولاً اولویت با درصد است.' WHERE `key` = 'fee_cash'; -UPDATE `settings` SET `description` = 'درصد کارمزد هر سفارش از مبلغ معامله (اعشاری، مثال 0.005 یعنی ۰٫۵٪). روی payment_amount اعمال می‌شود.' WHERE `key` = 'fee_service_charge_percentage'; +UPDATE `settings` SET `description` = 'روشن: خارج از بازه «ساعت شروع» تا «ساعت پایان» معامله و نمایش قیمت بسته می‌شود. خاموش: محدودیت ساعتی سراسری اعمال نمی‌شود.' WHERE `key` = 'close_price'; +UPDATE `settings` SET `description` = 'ساعت شروع فعالیت روزانه. فقط وقتی «بستن خودکار خارج از ساعت کاری» روشن باشد اعمال می‌شود (مثلاً ۰۹:۰۰).' WHERE `key` = 'startDay'; +UPDATE `settings` SET `description` = 'ساعت پایان فعالیت روزانه. فقط وقتی «بستن خودکار خارج از ساعت کاری» روشن باشد اعمال می‌شود (مثلاً ۲۲:۰۰).' WHERE `key` = 'endDay'; +UPDATE `settings` SET `description` = 'اختلاف پایه (ریال) بین قیمت خرید و فروش روی مظنه. معمولاً نصف فاصله اسپرد کل است.' WHERE `key` = 'fee_single'; +UPDATE `settings` SET `description` = 'مبلغ اضافه (ریال) روی قیمت پایه برای سمت خرید. خالی = استفاده از «اختلاف پایه».' WHERE `key` = 'buy_fee'; +UPDATE `settings` SET `description` = 'مبلغ کسر (ریال) از قیمت پایه برای سمت فروش. خالی = استفاده از «اختلاف پایه».' WHERE `key` = 'sell_fee'; +UPDATE `settings` SET `description` = 'کارمزد ثابت ریالی هر سفارش (قدیمی). اگر «درصد کارمزد معامله» تنظیم شده باشد، معمولاً اولویت با درصد است.' WHERE `key` = 'fee_cash'; +UPDATE `settings` SET `description` = 'درصد کارمزد هر سفارش از مبلغ معامله (اعشاری؛ مثلاً ۰٫۰۰۵ یعنی نیم در هزار).' WHERE `key` = 'fee_service_charge_percentage'; UPDATE `settings` SET `description` = 'تعداد اولین سفارش‌های هر کاربر که از کارمزد معاف می‌شوند. ۰ = غیرفعال.' WHERE `key` = 'fee_free'; UPDATE `settings` SET `description` = 'حداقل مبلغ درخواست برداشت وجه (ریال). درخواست‌های کمتر رد می‌شوند.' WHERE `key` = 'min_withdraw'; UPDATE `settings` SET `description` = 'حداقل مبلغ اولین خرید هر کاربر (ریال). برای فعال‌سازی کیف یا احراز سطح اول استفاده می‌شود.' WHERE `key` = 'min_first_buy'; 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 6af90fa..10b4f11 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 @@ -945,14 +945,14 @@ UPDATE `settings` SET `description` = 'شناسه عددی یا نام reading_m UPDATE `settings` SET `description` = 'آدرس پایه API سرویس تابلو آنلاین (بدون مسیر انتهایی). مثال: https://example.com/api' WHERE `key` = 'zaarbox_api_url'; UPDATE `settings` SET `description` = 'کلید یا UUID ردیف قیمت در پاسخ API تابلو. باید با محصول/ردیف موردنظر در سرویس خارجی یکسان باشد.' WHERE `key` = 'zaarbox_price_key'; UPDATE `settings` SET `description` = 'روشن: اگر API قیمت در دسترس نباشد، آخرین قیمت ذخیره‌شده در دیتابیس به کاربر نشان داده می‌شود. خاموش: در قطعی API ممکن است قیمت صفر نمایش داده شود.' WHERE `key` = 'show_last_price'; -UPDATE `settings` SET `description` = 'روشن: خارج از بازه startDay تا endDay معامله و نمایش قیمت بسته می‌شود. خاموش: محدودیت ساعتی اعمال نمی‌شود.' WHERE `key` = 'close_price'; -UPDATE `settings` SET `description` = 'ساعت شروع بازه معامله روزانه (فرمت 24 ساعته، مثال 09:00). فقط وقتی close_price روشن باشد اثر دارد.' WHERE `key` = 'startDay'; -UPDATE `settings` SET `description` = 'ساعت پایان بازه معامله روزانه (فرمت 24 ساعته، مثال 22:00). فقط وقتی close_price روشن باشد اثر دارد.' WHERE `key` = 'endDay'; -UPDATE `settings` SET `description` = 'اختلاف پایه (ریال) برای محاسبه قیمت خرید و فروش نمایشی روی مظنه. معمولاً نصف فاصله اسپرد کل است.' WHERE `key` = 'fee_single'; -UPDATE `settings` SET `description` = 'مبلغ اضافه (ریال) روی قیمت پایه برای نمایش/محاسبه سمت خرید. خالی = استفاده از fee_single.' WHERE `key` = 'buy_fee'; -UPDATE `settings` SET `description` = 'مبلغ کسر (ریال) از قیمت پایه برای نمایش/محاسبه سمت فروش. خالی = استفاده از fee_single.' WHERE `key` = 'sell_fee'; -UPDATE `settings` SET `description` = 'کارمزد ثابت ریالی هر سفارش (legacy). در صورت تنظیم fee_service_charge_percentage معمولاً اولویت با درصد است.' WHERE `key` = 'fee_cash'; -UPDATE `settings` SET `description` = 'درصد کارمزد هر سفارش از مبلغ معامله (اعشاری، مثال 0.005 یعنی ۰٫۵٪). روی payment_amount اعمال می‌شود.' WHERE `key` = 'fee_service_charge_percentage'; +UPDATE `settings` SET `description` = 'روشن: خارج از بازه «ساعت شروع» تا «ساعت پایان» معامله و نمایش قیمت بسته می‌شود. خاموش: محدودیت ساعتی سراسری اعمال نمی‌شود.' WHERE `key` = 'close_price'; +UPDATE `settings` SET `description` = 'ساعت شروع فعالیت روزانه. فقط وقتی «بستن خودکار خارج از ساعت کاری» روشن باشد اعمال می‌شود (مثلاً ۰۹:۰۰).' WHERE `key` = 'startDay'; +UPDATE `settings` SET `description` = 'ساعت پایان فعالیت روزانه. فقط وقتی «بستن خودکار خارج از ساعت کاری» روشن باشد اعمال می‌شود (مثلاً ۲۲:۰۰).' WHERE `key` = 'endDay'; +UPDATE `settings` SET `description` = 'اختلاف پایه (ریال) بین قیمت خرید و فروش روی مظنه. معمولاً نصف فاصله اسپرد کل است.' WHERE `key` = 'fee_single'; +UPDATE `settings` SET `description` = 'مبلغ اضافه (ریال) روی قیمت پایه برای سمت خرید. خالی = استفاده از «اختلاف پایه».' WHERE `key` = 'buy_fee'; +UPDATE `settings` SET `description` = 'مبلغ کسر (ریال) از قیمت پایه برای سمت فروش. خالی = استفاده از «اختلاف پایه».' WHERE `key` = 'sell_fee'; +UPDATE `settings` SET `description` = 'کارمزد ثابت ریالی هر سفارش (قدیمی). اگر «درصد کارمزد معامله» تنظیم شده باشد، معمولاً اولویت با درصد است.' WHERE `key` = 'fee_cash'; +UPDATE `settings` SET `description` = 'درصد کارمزد هر سفارش از مبلغ معامله (اعشاری؛ مثلاً ۰٫۰۰۵ یعنی نیم در هزار).' WHERE `key` = 'fee_service_charge_percentage'; UPDATE `settings` SET `description` = 'تعداد اولین سفارش‌های هر کاربر که از کارمزد معاف می‌شوند. ۰ = غیرفعال.' WHERE `key` = 'fee_free'; UPDATE `settings` SET `description` = 'حداقل مبلغ درخواست برداشت وجه (ریال). درخواست‌های کمتر رد می‌شوند.' WHERE `key` = 'min_withdraw'; UPDATE `settings` SET `description` = 'حداقل مبلغ اولین خرید هر کاربر (ریال). برای فعال‌سازی کیف یا احراز سطح اول استفاده می‌شود.' WHERE `key` = 'min_first_buy';