added assets into stream

This commit is contained in:
2026-06-27 15:51:44 +03:30
parent a0b3d8d3f5
commit 7b05db0ff0
6 changed files with 170 additions and 16 deletions
@@ -24,6 +24,7 @@ use App\Models\TransactionsAdmin;
use App\Services\BalanceReportService;
use App\Services\ConfigService;
use App\Services\WalletBalanceService;
use App\Services\WalletBroadcastService;
use Hekmatinasser\Verta\Verta;
@@ -154,6 +155,8 @@ class WalletController extends Controller
. "\n"
. $this->Config('name_fa');
$this->Send($customer->mobile, $message);
$this->broadcastCustomerWalletAssets($customer);
return response()->json(true);
}
public function charge_gold(Request $request)
@@ -224,6 +227,8 @@ class WalletController extends Controller
. $data['note']
. $this->Config('name_fa');
$this->Send($customer->mobile, $message);
$this->broadcastCustomerWalletAssets($customer);
return response()->json(true);
}
@@ -367,6 +372,7 @@ class WalletController extends Controller
. "\n"
. $this->Config('name_fa');
$this->Send($customer->mobile, $message);
$this->broadcastCustomerWalletAssets($customer);
return response()->json(true);
}
@@ -467,6 +473,10 @@ class WalletController extends Controller
$transaction->save();
$message = 'تراکنش با موفقیت به‌روزرسانی شد.';
$customer = Customer::find($transaction->customer_id);
if ($customer) {
$this->broadcastCustomerWalletAssets($customer);
}
return response()->json([
'message' => $message,
@@ -660,4 +670,12 @@ class WalletController extends Controller
]);
}
private function broadcastCustomerWalletAssets(Customer $customer): void
{
app(WalletBroadcastService::class)->broadcastForCustomer(
(int) $customer->id,
(int) $customer->type
);
}
}
@@ -25,6 +25,7 @@ use App\Models\Setting;
use App\Models\Cnt;
use App\Services\MontzarService;
use App\Services\ConfigService;
use App\Services\WalletBroadcastService;
class SingleOrderController extends Controller
@@ -221,6 +222,14 @@ class SingleOrderController extends Controller
report($e);
}
}
if ($customer) {
app(WalletBroadcastService::class)->broadcastForCustomer(
(int) $customer->id,
$ownerType
);
}
return response()->json([$response, $data]);
}
@@ -3,6 +3,7 @@
namespace App\Http\Controllers\Site;
use App\Http\Controllers\Controller;
use App\Services\AssetSummaryService;
use App\Services\LivePriceCacheService;
use App\Services\PriceBroadcastService;
use App\Services\PriceStreamService;
@@ -19,7 +20,11 @@ class PriceStreamController extends Controller
abort(404);
}
return response()->stream(function () use ($service) {
$customer = auth('customer')->user();
$customerId = $customer ? (int) $customer->id : 0;
$customerOwnerType = $customer ? (int) $customer->type : 0;
return response()->stream(function () use ($service, $customerId, $customerOwnerType) {
@set_time_limit(0);
if (function_exists('apache_setenv')) {
@@ -50,6 +55,18 @@ class PriceStreamController extends Controller
$send('list', $list);
}
$customerLastId = 0;
if ($customerId > 0) {
$assets = app(AssetSummaryService::class)->buildForCustomer(
$customerId,
$customerOwnerType > 0 ? $customerOwnerType : 2
);
if ($assets !== []) {
$send('assets', $assets);
}
$customerLastId = $service->currentCustomerSeq($customerId);
}
$lastId = $service->currentSeq();
$heartbeatTicks = 0;
@@ -62,6 +79,13 @@ class PriceStreamController extends Controller
$lastId = (int) $event['id'];
}
if ($customerId > 0) {
foreach ($service->consumeCustomerSince($customerId, $customerLastId) as $event) {
$send((string) $event['event'], $event['data']);
$customerLastId = (int) $event['id'];
}
}
$heartbeatTicks++;
// هر ۵ ثانیه — LiteSpeed/پروکسی اتصال بیکار را نمی‌بندد
if ($heartbeatTicks >= 10) {
+83 -14
View File
@@ -9,6 +9,8 @@ class PriceStreamService
{
private const INBOX_PREFIX = 'price_sse_inbox:';
private const CUSTOMER_INBOX_PREFIX = 'price_sse_inbox:customer:';
private const INBOX_TTL_SECONDS = 300;
private const MAX_EVENTS = 50;
@@ -70,9 +72,16 @@ class PriceStreamService
public function currentSeq(): int
{
$inbox = Cache::get($this->inboxKey(), ['seq' => 0, 'events' => []]);
return $this->currentInboxSeq($this->inboxKey());
}
return (int) ($inbox['seq'] ?? 0);
public function currentCustomerSeq(int $customerId): int
{
if ($customerId <= 0) {
return 0;
}
return $this->currentInboxSeq($this->customerInboxKey($customerId));
}
/**
@@ -80,13 +89,19 @@ class PriceStreamService
*/
public function consumeSince(int $afterId): array
{
$inbox = Cache::get($this->inboxKey(), ['seq' => 0, 'events' => []]);
$events = is_array($inbox['events'] ?? null) ? $inbox['events'] : [];
return $this->consumeInboxSince($this->inboxKey(), $afterId);
}
return array_values(array_filter(
$events,
static fn (array $event): bool => (int) ($event['id'] ?? 0) > $afterId
));
/**
* @return array<int, array{id: int, event: string, data: mixed}>
*/
public function consumeCustomerSince(int $customerId, int $afterId): array
{
if ($customerId <= 0) {
return [];
}
return $this->consumeInboxSince($this->customerInboxKey($customerId), $afterId);
}
/**
@@ -98,8 +113,57 @@ class PriceStreamService
return;
}
$key = $this->inboxKey();
$this->publishToInbox($this->inboxKey(), $event, $data);
}
/**
* @param array<string, mixed>|array<int, mixed> $data
*/
public function publishForCustomer(int $customerId, string $event, array $data): void
{
if (! $this->isEnabled() || $customerId <= 0) {
return;
}
$this->publishToInbox($this->customerInboxKey($customerId), $event, $data);
}
private function inboxKey(): string
{
return self::INBOX_PREFIX.'global';
}
private function customerInboxKey(int $customerId): string
{
return self::CUSTOMER_INBOX_PREFIX.$customerId;
}
private function currentInboxSeq(string $key): int
{
$inbox = Cache::get($key, ['seq' => 0, 'events' => []]);
return (int) ($inbox['seq'] ?? 0);
}
/**
* @return array<int, array{id: int, event: string, data: mixed}>
*/
private function consumeInboxSince(string $key, int $afterId): array
{
$inbox = Cache::get($key, ['seq' => 0, 'events' => []]);
$events = is_array($inbox['events'] ?? null) ? $inbox['events'] : [];
return array_values(array_filter(
$events,
static fn (array $event): bool => (int) ($event['id'] ?? 0) > $afterId
));
}
/**
* @param array<string, mixed>|array<int, mixed> $data
*/
private function publishToInbox(string $key, string $event, array $data): void
{
Cache::lock($key.':lock', 5)->block(3, function () use ($key, $event, $data): void {
$inbox = Cache::get($key, ['seq' => 0, 'events' => []]);
$events = is_array($inbox['events'] ?? null) ? $inbox['events'] : [];
@@ -126,11 +190,6 @@ class PriceStreamService
});
}
private function inboxKey(): string
{
return self::INBOX_PREFIX.'global';
}
/**
* @param array<string, mixed>|array<int, mixed> $data
*/
@@ -148,6 +207,16 @@ class PriceStreamService
return md5(json_encode(PriceBroadcastService::listIdentity($data), JSON_UNESCAPED_UNICODE));
}
if ($event === 'assets') {
return md5(json_encode([
'cash_wallet' => (int) ($data['cash_wallet'] ?? 0),
'total_rial_value' => (int) ($data['total_rial_value'] ?? 0),
'assets_rial_value' => (int) ($data['assets_rial_value'] ?? 0),
'inventory' => md5(json_encode($data['inventory_cards'] ?? [], JSON_UNESCAPED_UNICODE)),
'metals' => md5(json_encode($data['metal_totals'] ?? [], JSON_UNESCAPED_UNICODE)),
], JSON_UNESCAPED_UNICODE));
}
return md5(json_encode($data, JSON_UNESCAPED_UNICODE));
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace App\Services;
use App\Models\Customer;
class WalletBroadcastService
{
public function broadcastForCustomer(int $customerId, ?int $ownerType = null): void
{
$stream = app(PriceStreamService::class);
if (! $stream->isEnabled() || $customerId <= 0) {
return;
}
if ($ownerType === null || $ownerType <= 0) {
$ownerType = (int) Customer::query()->whereKey($customerId)->value('type');
}
if ($ownerType <= 0) {
$ownerType = 2;
}
$payload = app(AssetSummaryService::class)->buildForCustomer($customerId, $ownerType);
$stream->publishForCustomer($customerId, 'assets', $payload);
}
}
+8 -1
View File
@@ -30,7 +30,14 @@ use App\Http\Controllers\Admin\CategoryController;
Route::get('/candle',[ReportController::class, 'candleChart']);
Route::get('/price/up',[ReportController::class, 'upDownPrice']);
Route::match(['get', 'post'],'/price/last',[ReportController::class, 'lastPrice']);
Route::get('/price/stream', [PriceStreamController::class, 'stream'])->withoutMiddleware(['throttle:api']);
$priceStreamRoute = Route::get('/price/stream', [PriceStreamController::class, 'stream'])
->withoutMiddleware(['throttle:api']);
if (! app(ConfigService::class)->isStore()) {
$priceStreamRoute->middleware('auth:customer');
}
Route::get('/price/updatePrise',[ReportController::class, 'updatePrise']);
Route::get('/const/key/{key}',[ConstController::class, 'showValue']);
Route::get('/check',[ReportController::class, 'check']);