fix(price): read last quote and daily extremes from the active provider
A stray montzar tick landed in the prices table, which stores no source, so the database_last fallback served a ~168M quote on a shop whose Zaarbox board was at ~217M. Last price and today's high/low now come from price_sync_logs filtered by price_provider, the database fallback no longer counts as today's price, and daily extremes reset when the provider changes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5,9 +5,11 @@ namespace App\Services;
|
||||
use App\Models\Cnt;
|
||||
use App\Models\Price;
|
||||
use App\Models\PriceCandle;
|
||||
use App\Models\PriceSyncLog;
|
||||
use App\Models\Product;
|
||||
use App\Models\Setting;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||||
|
||||
class ConfigService
|
||||
@@ -37,13 +39,65 @@ class ConfigService
|
||||
}
|
||||
|
||||
/**
|
||||
* آخرین قیمت ذخیرهشده در جدول prices.
|
||||
* آخرین قیمت مرجع ثبتشده از همین provider (price_sync_logs).
|
||||
*
|
||||
* جدول prices منبع قیمت را نگه نمیدارد؛ یک تیک از provider دیگر
|
||||
* (مثلاً montzar روی فروشگاهی که تابلوی zaarbox دارد) تا روزها
|
||||
* «آخرین قیمت» میماند و مظنه پرت نشان داده میشود.
|
||||
*/
|
||||
public function referenceQuotePrice(): float
|
||||
public function lastProviderQuotePrice(): int
|
||||
{
|
||||
$last = Price::query()->orderByDesc('id')->value('price');
|
||||
$provider = $this->priceProvider();
|
||||
|
||||
return $last !== null ? (float) $last : 0.0;
|
||||
return (int) Cache::remember(
|
||||
'price:last_provider_quote:' . $provider,
|
||||
10,
|
||||
function () use ($provider) {
|
||||
try {
|
||||
return (int) PriceSyncLog::query()
|
||||
->where('source', $provider)
|
||||
->where('liquid_gold_price', '>', 0)
|
||||
->orderByDesc('id')
|
||||
->value('liquid_gold_price');
|
||||
} catch (\Throwable $e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* کمینه/بیشینهٔ قیمت مرجع امروز — فقط ردیفهای همین provider.
|
||||
*
|
||||
* @return array{min: int, max: int}
|
||||
*/
|
||||
public function providerTodayQuoteExtremes(): array
|
||||
{
|
||||
$provider = $this->priceProvider();
|
||||
|
||||
$row = Cache::remember(
|
||||
'price:provider_today_extremes:' . $provider,
|
||||
10,
|
||||
function () use ($provider) {
|
||||
try {
|
||||
$stats = PriceSyncLog::query()
|
||||
->where('source', $provider)
|
||||
->where('liquid_gold_price', '>', 0)
|
||||
->whereDate('created_at', now()->toDateString())
|
||||
->selectRaw('MIN(liquid_gold_price) as min_price, MAX(liquid_gold_price) as max_price')
|
||||
->first();
|
||||
|
||||
return [
|
||||
'min' => (int) ($stats->min_price ?? 0),
|
||||
'max' => (int) ($stats->max_price ?? 0),
|
||||
];
|
||||
} catch (\Throwable $e) {
|
||||
return ['min' => 0, 'max' => 0];
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return is_array($row) ? $row : ['min' => 0, 'max' => 0];
|
||||
}
|
||||
|
||||
/** مظنهٔ مثبت قابل استفاده است — محدودیت انحراف نسبت به DB برداشته شد. */
|
||||
@@ -542,18 +596,24 @@ class ConfigService
|
||||
|
||||
$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) {
|
||||
// سقف و کف روز باید از یک منبع باشند؛ وگرنه تیک provider دیگر
|
||||
// بازهٔ جعلی میسازد (مثلاً سقف تابلو و کف montzar).
|
||||
$provider = $this->priceProvider();
|
||||
$extremeSource = trim((string) $this->getString('price_extreme_source', ''));
|
||||
|
||||
if ($extremeDate !== $today || ($extremeSource !== '' && $extremeSource !== $provider)) {
|
||||
$this->UpdateConfig('up_price', $priceInt);
|
||||
$this->UpdateConfig('down_price', $priceInt);
|
||||
$this->UpdateConfig('price_extreme_date', $today);
|
||||
$this->UpdateConfig('price_extreme_source', $provider);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($extremeSource === '') {
|
||||
$this->UpdateConfig('price_extreme_source', $provider);
|
||||
}
|
||||
|
||||
if ($currentUp <= 0 || $priceInt > $currentUp) {
|
||||
$this->UpdateConfig('up_price', $priceInt);
|
||||
}
|
||||
@@ -569,6 +629,12 @@ class ConfigService
|
||||
*/
|
||||
public function resolvedDailyBaseExtremes(): array
|
||||
{
|
||||
// تاریخچهٔ برچسبدار همین provider — سقف/کف بدون قاطی شدن با تیک منبع دیگر
|
||||
$fromLog = $this->providerTodayQuoteExtremes();
|
||||
if ($fromLog['max'] > 0 && $fromLog['min'] > 0) {
|
||||
return ['up' => $fromLog['max'], 'down' => $fromLog['min']];
|
||||
}
|
||||
|
||||
$up = $this->getInt('up_price', 0);
|
||||
$down = $this->getInt('down_price', 0);
|
||||
|
||||
@@ -604,21 +670,8 @@ 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 && $this->isPlausibleQuotePrice((float) $down, (float) max($up, 1))) {
|
||||
if ($down > 0 && $down < $storedDown) {
|
||||
$this->UpdateConfig('down_price', $down);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user