fix some bugs
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\AppNotification;
|
||||
use App\Models\AppNotificationRead;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
/**
|
||||
* تعداد خواندهنشده + پخش SSE رویداد unread-count برای اپ کاربر.
|
||||
*/
|
||||
class AppNotificationUnreadService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ConfigService $config,
|
||||
private readonly PriceStreamService $stream,
|
||||
) {
|
||||
}
|
||||
|
||||
public function featureEnabled(): bool
|
||||
{
|
||||
return $this->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)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user