where('is_active', true)->get(); if ($triggers->isEmpty()) { return; } $config = app(ConfigService::class); $display = app(ProductDisplayPriceService::class); $lastPrice = Price::query()->latest('id')->first(); $boardPrice = (float) ($payload['price'] ?? $lastPrice?->price ?? 0); $productsById = collect(); if (PriceTrigger::hasProductIdColumn()) { $ids = $triggers->pluck('product_id')->filter()->unique()->all(); if ($ids !== []) { $productsById = Product::query()->whereIn('id', $ids)->get()->keyBy('id'); if ($productsById->isNotEmpty()) { $display->warmLivePriceCache(); $display->enrichForApi($productsById); } } } foreach ($triggers as $trigger) { try { $this->tryFire($trigger, $productsById, $display, $config, $boardPrice); } catch (\Throwable $e) { Log::warning('Price trigger evaluate failed', [ 'id' => $trigger->id, 'message' => $e->getMessage(), ]); } } } private function tryFire( PriceTrigger $trigger, $productsById, ProductDisplayPriceService $display, ConfigService $config, float $boardPrice ): void { $product = $this->resolveProduct($trigger, $productsById); $current = $this->resolveCurrentPrice($trigger, $product, $display, $config, $boardPrice); if ($current <= 0) { return; } if (! $this->conditionMet((string) $trigger->price_condition, $current, (float) $trigger->target_price)) { return; } $customer = Customer::query()->find($trigger->customer_id); if (! $customer) { return; } $this->notify($trigger, $customer, $product, $current); $trigger->is_active = false; $trigger->triggered_at = now(); $trigger->save(); } private function resolveProduct(PriceTrigger $trigger, $productsById): ?Product { if (! PriceTrigger::hasProductIdColumn()) { return null; } $productId = (int) ($trigger->product_id ?? 0); if ($productId <= 0) { return null; } $product = $productsById->get($productId); if ($product instanceof Product) { return $product; } return Product::query()->find($productId); } private function resolveCurrentPrice( PriceTrigger $trigger, ?Product $product, ProductDisplayPriceService $display, ConfigService $config, float $boardPrice ): float { if ($product instanceof Product) { $enriched = (int) (($product->price ?? 0) + ($product->price_profit ?? 0)); if ($enriched > 0) { return (float) $enriched; } if ($display->usesAppPrice($product)) { $base = $boardPrice > 0 ? $boardPrice : $display->resolveOrderBasePrice($product); if ($base <= 0) { return 0.0; } $trade = $config->buildTradeDisplayPrices((float) $base, $config->priceProvider()); $direction = (int) ($trigger->direction ?? 0); return (float) max(0, (int) ($direction === 2 ? $trade['sell'] : $trade['buy'])); } return max(0.0, (float) $display->resolveOrderBasePrice($product)); } if ($boardPrice <= 0) { return 0.0; } $direction = (int) ($trigger->direction ?? $trigger->dirction ?? 0); $diff = $direction === 1 ? $config->displaySellPriceDiffRial() : ($direction === 2 ? $config->displayBuyPriceDiffRial() : 0.0); if ($direction === 1) { return max(0.0, $boardPrice - $diff); } if ($direction === 2) { return $boardPrice + $diff; } return $boardPrice; } private function conditionMet(string $op, float $current, float $target): bool { return match ($op) { '>' => $current > $target, '<' => $current < $target, '>=', 'gte' => $current >= $target, '<=', 'lte' => $current <= $target, '==', '=' => abs($current - $target) < 1, default => false, }; } private function notify(PriceTrigger $trigger, Customer $customer, ?Product $product, float $current): void { $title = trim((string) ($product?->title ?? '')); if ($title === '') { $title = 'طلا'; } $shopName = (string) Controller::Config('name_fa'); $message = 'قیمت ' . $title . ' به ' . number_format((int) $current) . " رسید\n" . $shopName; if (! Controller::isSmsNotifEnabled('sms_notif_price_trigger')) { return; } if (trim((string) ($customer->mobile ?? '')) === '') { return; } Controller::Send($customer->mobile, $message); } }