diff --git a/app/Http/Controllers/Admin/AppNotificationController.php b/app/Http/Controllers/Admin/AppNotificationController.php index 99a1b41..e199fed 100644 --- a/app/Http/Controllers/Admin/AppNotificationController.php +++ b/app/Http/Controllers/Admin/AppNotificationController.php @@ -309,6 +309,7 @@ class AppNotificationController extends Controller ]; $stream = app(PriceStreamService::class); + $unread = app(\App\Services\AppNotificationUnreadService::class); if ($row->audience_type === AppNotificationAudienceService::TYPE_ALL) { $stream->publish('app_notification', $payload); @@ -321,7 +322,11 @@ class AppNotificationController extends Controller ->pluck('customer_id'); foreach ($ids as $customerId) { - $stream->publishForCustomer((int) $customerId, 'app_notification', $payload); + $cid = (int) $customerId; + $stream->publishForCustomer($cid, 'app_notification', array_merge($payload, [ + 'unread_count' => $unread->countForCustomer($cid), + ])); + $unread->publishForCustomer($cid); } } } diff --git a/app/Http/Controllers/Customer/AppNotificationController.php b/app/Http/Controllers/Customer/AppNotificationController.php index 4ab374f..5d94467 100644 --- a/app/Http/Controllers/Customer/AppNotificationController.php +++ b/app/Http/Controllers/Customer/AppNotificationController.php @@ -5,36 +5,22 @@ namespace App\Http\Controllers\Customer; use App\Http\Controllers\Controller; use App\Models\AppNotification; use App\Models\AppNotificationRead; -use App\Services\AppNotificationAudienceService; -use App\Services\ConfigService; -use Illuminate\Database\Eloquent\Builder; +use App\Services\AppNotificationUnreadService; use Illuminate\Http\Request; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class AppNotificationController extends Controller { - private function assertFeatureEnabled(): void - { - if (! app(ConfigService::class)->getBool('has_page_notification', true)) { - throw new NotFoundHttpException('اعلان داخل اپ غیرفعال است.'); - } + public function __construct( + private readonly AppNotificationUnreadService $unread, + ) { } - private function visibleQuery(int $customerId): Builder + private function assertFeatureEnabled(): void { - return AppNotification::query() - ->where('is_active', true) - ->where(function (Builder $q) use ($customerId) { - $q->where('audience_type', AppNotificationAudienceService::TYPE_ALL) - ->orWhere(function (Builder $inner) use ($customerId) { - $inner->whereIn('audience_type', [ - AppNotificationAudienceService::TYPE_SEGMENT, - AppNotificationAudienceService::TYPE_CUSTOMERS, - ])->whereHas('recipients', function (Builder $r) use ($customerId) { - $r->where('customer_id', $customerId); - }); - }); - }); + if (! $this->unread->featureEnabled()) { + throw new NotFoundHttpException('اعلان داخل اپ غیرفعال است.'); + } } public function index() @@ -42,7 +28,7 @@ class AppNotificationController extends Controller $this->assertFeatureEnabled(); $customerId = (int) auth()->id(); - $rows = $this->visibleQuery($customerId) + $rows = $this->unread->visibleQuery($customerId) ->orderByDesc('id') ->limit(100) ->get(['id', 'title', 'body', 'severity', 'created_at']); @@ -73,16 +59,7 @@ class AppNotificationController extends Controller $this->assertFeatureEnabled(); $customerId = (int) auth()->id(); - $visibleIds = $this->visibleQuery($customerId)->select('id'); - $total = (clone $visibleIds)->count(); - $read = AppNotificationRead::query() - ->where('customer_id', $customerId) - ->whereIn('notification_id', $visibleIds) - ->count(); - - return response()->json([ - 'unread_count' => max(0, $total - $read), - ]); + return response()->json($this->unread->payloadForCustomer($customerId)); } public function markRead(Request $request, int $id) @@ -90,7 +67,7 @@ class AppNotificationController extends Controller $this->assertFeatureEnabled(); $customerId = (int) auth()->id(); - $exists = $this->visibleQuery($customerId)->where('id', $id)->exists(); + $exists = $this->unread->visibleQuery($customerId)->where('id', $id)->exists(); if (! $exists) { throw new NotFoundHttpException('اعلان یافت نشد.'); } @@ -105,6 +82,8 @@ class AppNotificationController extends Controller ] ); + $this->unread->publishForCustomer($customerId); + return response()->json(true); } @@ -113,7 +92,7 @@ class AppNotificationController extends Controller $this->assertFeatureEnabled(); $customerId = (int) auth()->id(); - $ids = $this->visibleQuery($customerId)->pluck('id'); + $ids = $this->unread->visibleQuery($customerId)->pluck('id'); $now = now(); foreach ($ids as $notificationId) { AppNotificationRead::query()->updateOrCreate( @@ -127,6 +106,8 @@ class AppNotificationController extends Controller ); } + $this->unread->publishForCustomer($customerId); + return response()->json(true); } } diff --git a/app/Http/Controllers/Single/SingleOrderController.php b/app/Http/Controllers/Single/SingleOrderController.php index 7d11fc2..8fa0a59 100644 --- a/app/Http/Controllers/Single/SingleOrderController.php +++ b/app/Http/Controllers/Single/SingleOrderController.php @@ -392,8 +392,8 @@ class SingleOrderController extends Controller private function processBuyTransactionProduct(&$data, $wallet, $wallet_interface) { - $data['price'] = $data['product']['price'] + $data['product']['price_profit']; - $data['price'] += $data['difference']; + // همان قیمت پایهٔ سفارش (price_original / قیمت اپ) — نه ستون کهنهٔ products.price + $data['price'] = $this->resolveProductOrderUnitPrice($data) + (float) ($data['difference'] ?? 0); $data['amount'] = $data['number'] * $data['product']['amount']; // محصول تعدادی: کارمزد فقط از ریال (fee_service_charge_percentage) $feeRate = isset($data['fee_free']) && $data['fee_free'] === true @@ -662,8 +662,8 @@ class SingleOrderController extends Controller private function processSellTransactionProduct(&$data, $wallet, $wallet_interface) { - $data['price'] = $data['product']['price'] + $data['product']['price_profit']; - $data['price'] -= $data['difference']; + // همان قیمت پایهٔ سفارش (price_original / قیمت اپ) — نه ستون کهنهٔ products.price + $data['price'] = max(0.0, $this->resolveProductOrderUnitPrice($data) - (float) ($data['difference'] ?? 0)); $data['amount'] = $data['number'] * $data['product']['amount']; // محصول تعدادی: کارمزد فقط از ریال $feeRate = isset($data['fee_free']) && $data['fee_free'] === true @@ -1118,6 +1118,27 @@ class SingleOrderController extends Controller ); } + /** + * قیمت پایهٔ هر گرم/واحد برای سفارش محصول — اول price_original (زنده)، بعد DB. + */ + private function resolveProductOrderUnitPrice(array $data): float + { + $base = (float) ($data['price_original'] ?? 0); + if ($base > 0) { + return $base; + } + + $product = $data['product'] ?? null; + if ($product instanceof Product) { + return (float) (($product->price ?? 0) + ($product->price_profit ?? 0)); + } + if (is_array($product)) { + return (float) (($product['price'] ?? 0) + ($product['price_profit'] ?? 0)); + } + + return (float) ($data['price'] ?? 0); + } + private function resolveSellOrderCashRial(array $data): ?float { $targetCash = $this->resolveTargetCash($data); @@ -1128,10 +1149,9 @@ class SingleOrderController extends Controller if (!empty($data['product_id']) && isset($data['number'])) { $product = $data['product'] ?? Product::find($data['product_id']); if ($product) { - $unitPrice = (float) ($product->price + $product->price_profit); $difference = (float) ($data['difference'] ?? 0); $amount = (float) $data['number'] * (float) $product->amount; - $sellUnitPrice = max(0.0, $unitPrice - $difference); + $sellUnitPrice = max(0.0, $this->resolveProductOrderUnitPrice($data) - $difference); return $amount * $sellUnitPrice; } @@ -1140,9 +1160,8 @@ class SingleOrderController extends Controller if (isset($data['amount'])) { $amount = (float) $data['amount']; if ($amount > 0) { - $price = (float) ($data['price'] ?? 0); $difference = (float) ($data['difference'] ?? 0); - $sellUnitPrice = max(0.0, $price - $difference); + $sellUnitPrice = max(0.0, $this->resolveProductOrderUnitPrice($data) - $difference); return $amount * $sellUnitPrice; } diff --git a/app/Http/Controllers/Site/PriceStreamController.php b/app/Http/Controllers/Site/PriceStreamController.php index 4c6c7ed..d148e12 100644 --- a/app/Http/Controllers/Site/PriceStreamController.php +++ b/app/Http/Controllers/Site/PriceStreamController.php @@ -71,6 +71,12 @@ class PriceStreamController extends Controller if ($assets !== []) { $send('assets', $assets); } + + $unread = app(\App\Services\AppNotificationUnreadService::class); + if ($unread->featureEnabled()) { + $send('unread-count', $unread->payloadForCustomer($customerId)); + } + $customerLastId = $service->currentCustomerSeq($customerId); } diff --git a/app/Services/AppNotificationUnreadService.php b/app/Services/AppNotificationUnreadService.php new file mode 100644 index 0000000..289d507 --- /dev/null +++ b/app/Services/AppNotificationUnreadService.php @@ -0,0 +1,80 @@ +config->getBool('has_page_notification', true); + } + + public function visibleQuery(int $customerId): Builder + { + return AppNotification::query() + ->where('is_active', true) + ->where(function (Builder $q) use ($customerId) { + $q->where('audience_type', AppNotificationAudienceService::TYPE_ALL) + ->orWhere(function (Builder $inner) use ($customerId) { + $inner->whereIn('audience_type', [ + AppNotificationAudienceService::TYPE_SEGMENT, + AppNotificationAudienceService::TYPE_CUSTOMERS, + ])->whereHas('recipients', function (Builder $r) use ($customerId) { + $r->where('customer_id', $customerId); + }); + }); + }); + } + + public function countForCustomer(int $customerId): int + { + if ($customerId <= 0 || ! $this->featureEnabled()) { + return 0; + } + + $visibleIds = $this->visibleQuery($customerId)->select('id'); + $total = (clone $visibleIds)->count(); + $read = AppNotificationRead::query() + ->where('customer_id', $customerId) + ->whereIn('notification_id', $visibleIds) + ->count(); + + return max(0, $total - $read); + } + + /** + * @return array{unread_count: int} + */ + public function payloadForCustomer(int $customerId): array + { + return [ + 'unread_count' => $this->countForCustomer($customerId), + ]; + } + + public function publishForCustomer(int $customerId): void + { + if ($customerId <= 0 || ! $this->featureEnabled() || ! $this->stream->isEnabled()) { + return; + } + + $this->stream->publishForCustomer( + $customerId, + 'unread-count', + $this->payloadForCustomer($customerId) + ); + } +} diff --git a/app/Services/CustomerAppNotifyService.php b/app/Services/CustomerAppNotifyService.php index b58bc00..8a8b232 100644 --- a/app/Services/CustomerAppNotifyService.php +++ b/app/Services/CustomerAppNotifyService.php @@ -89,14 +89,17 @@ class CustomerAppNotifyService private function broadcastToCustomer(AppNotification $row, int $customerId): void { + $unread = app(AppNotificationUnreadService::class); $payload = [ 'id' => $row->id, 'title' => $row->title, 'body' => $row->body, 'severity' => $row->severity, 'created_at' => optional($row->created_at)?->toIso8601String(), + 'unread_count' => $unread->countForCustomer($customerId), ]; $this->stream->publishForCustomer($customerId, 'app_notification', $payload); + $unread->publishForCustomer($customerId); } }