feat(settings): enable_automation gate; reject stale montzar quote jumps

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-24 16:15:52 +03:30
co-authored by Cursor
parent b28a98938e
commit 352bb9976d
9 changed files with 212 additions and 18 deletions
+68 -6
View File
@@ -14,11 +14,44 @@ class ConfigService
{
public const PRICE_PROVIDERS = ['montzar', 'main_product', 'zaarbox'];
/**
* حداکثر انحراف مجاز مظنه نسبت به آخرین قیمت معتبر DB.
* پرش اشتباه provider (مثل ۱۶۸M ماندهٔ montzar روی مظنهٔ ~۲۲۰M) را رد می‌کند.
*/
public const MAX_QUOTE_DEVIATION_RATIO = 0.15;
public function Config($name)
{
return $this->get($name, false);
}
/**
* آخرین قیمت ذخیره‌شده در جدول prices — مرجع سلامت مظنهٔ زنده.
*/
public function referenceQuotePrice(): float
{
$last = Price::query()->orderByDesc('id')->value('price');
return $last !== null ? (float) $last : 0.0;
}
/**
* مظنهٔ زنده وقتی خیلی از آخرین قیمت DB فاصله دارد قابل اتکا نیست (ماندهٔ provider اشتباه).
*/
public function isPlausibleQuotePrice(float $candidate, ?float $reference = null): bool
{
if ($candidate <= 0) {
return false;
}
$reference = $reference ?? $this->referenceQuotePrice();
if ($reference <= 0) {
return true;
}
return abs($candidate - $reference) / $reference <= self::MAX_QUOTE_DEVIATION_RATIO;
}
/**
* مقدار setting؛ اول سوپرادمین، بعد DB لوکال، بعد price_providers.php.
* برای کلیدهای can_edit=0 همیشه از این مسیر (یا ConfigService) بخوانید — نه Setting::where مستقیم.
@@ -281,7 +314,8 @@ class ConfigService
* اولویت:
* 1) price_provider (یا price_primary_source قدیمی) — فقط مقادیر معتبر
* 2) main_product_price=true → main_product (legacy)
* 3) پیش‌فرض فایل: montzar
* 3) اگر zaarbox_price_key تنظیم شده → zaarbox (نه پیش‌فرض montzar)
* 4) پیش‌فرض فایل
*/
public function priceProvider(): string
{
@@ -296,6 +330,10 @@ class ConfigService
return 'main_product';
}
if ($this->getString('zaarbox_price_key', '') !== '') {
return 'zaarbox';
}
$default = strtolower((string) config('price_providers.price_provider', 'montzar'));
return in_array($default, self::PRICE_PROVIDERS, true) ? $default : 'montzar';
@@ -473,6 +511,13 @@ class ConfigService
$today = now()->toDateString();
$extremeDate = $this->Config('price_extreme_date');
$currentUp = (float) ($upSetting->value ?: 0);
$currentDown = (float) ($downSetting->value ?: 0);
$anchor = $currentUp > 0 ? $currentUp : $this->referenceQuotePrice();
if ($anchor > 0 && ! $this->isPlausibleQuotePrice((float) $priceInt, $anchor)) {
return;
}
if ($extremeDate !== $today) {
$this->UpdateConfig('up_price', $priceInt);
$this->UpdateConfig('down_price', $priceInt);
@@ -480,9 +525,6 @@ class ConfigService
return;
}
$currentUp = (float) ($upSetting->value ?: 0);
$currentDown = (float) ($downSetting->value ?: 0);
if ($currentUp <= 0 || $priceInt > $currentUp) {
$this->UpdateConfig('up_price', $priceInt);
}
@@ -533,8 +575,21 @@ class ConfigService
[$up, $down] = [$down, $up];
}
// کف روز آلوده به مظنهٔ اشتباه provider (مثلاً ۱۶۸M) را دور بریز
if ($up > 0 && $down > 0 && ! $this->isPlausibleQuotePrice((float) $down, (float) $up)) {
$fromTable = $this->filteredTodayMinMax();
if ($fromTable['min'] > 0 && $this->isPlausibleQuotePrice((float) $fromTable['min'], (float) $up)) {
$down = $fromTable['min'];
} else {
$down = $up;
}
if ($this->getInt('down_price', 0) !== $down) {
$this->UpdateConfig('down_price', $down);
}
}
$storedDown = $this->getInt('down_price', 0);
if ($down > 0 && $down < $storedDown) {
if ($down > 0 && $down < $storedDown && $this->isPlausibleQuotePrice((float) $down, (float) max($up, 1))) {
$this->UpdateConfig('down_price', $down);
}
@@ -629,8 +684,9 @@ class ConfigService
public function applyMontzarSyncFailure(): void
{
$staleLive = $this->getInt('montzar_live_price', 0);
$usable = $staleLive > 0 && $this->isPlausibleQuotePrice((float) $staleLive);
$this->UpdateConfig('montzar_closed', $staleLive > 0 ? 'false' : 'true');
$this->UpdateConfig('montzar_closed', $usable ? 'false' : 'true');
}
/**
@@ -817,6 +873,12 @@ class ConfigService
return $this->getBool('enable_conditional_order', false);
}
/** سوییچ سوپرادمین اتوماسیون ادمین (پیش‌فرض خاموش). */
public function isAutomationEnabled(): bool
{
return $this->getBool('enable_automation', false);
}
/** سوییچ دیپلوی قلک per-product (can_edit=0، پیش‌فرض خاموش). */
public function isProductFundsEnabled(): bool
{