chart fixed
This commit is contained in:
@@ -31,12 +31,12 @@ class PriceSyncLogController extends Controller
|
|||||||
|
|
||||||
public function runSync(LivePriceTickService $tickService)
|
public function runSync(LivePriceTickService $tickService)
|
||||||
{
|
{
|
||||||
$tickService->tick();
|
$log = $tickService->tick();
|
||||||
|
|
||||||
$log = PriceSyncLog::with('items')->orderByDesc('id')->first();
|
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'message' => $log ? 'قیمتها بهروزرسانی شد' : 'همگامسازی انجام شد',
|
'message' => $log
|
||||||
|
? 'قیمتها بهروزرسانی شد'
|
||||||
|
: 'قیمت تغییری نکرد؛ رکورد جدیدی ثبت نشد',
|
||||||
'data' => $log,
|
'data' => $log,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ use App\Services\ConfigService;
|
|||||||
use App\Services\ProductDisplayPriceService;
|
use App\Services\ProductDisplayPriceService;
|
||||||
use App\Services\ProductPriceService;
|
use App\Services\ProductPriceService;
|
||||||
use App\Services\ProductPriceSyncService;
|
use App\Services\ProductPriceSyncService;
|
||||||
|
use App\Services\PriceBroadcastService;
|
||||||
use App\Services\CategoryService;
|
use App\Services\CategoryService;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
@@ -251,6 +252,10 @@ class ProductController extends SuperController
|
|||||||
app(ProductPriceSyncService::class)->syncAll(true);
|
app(ProductPriceSyncService::class)->syncAll(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dispatch(function () {
|
||||||
|
app(PriceBroadcastService::class)->broadcastAfterPriceUpdate();
|
||||||
|
})->afterResponse();
|
||||||
|
|
||||||
return response()->json(true);
|
return response()->json(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
use App\Http\Controllers\Site\ReportController;
|
use App\Http\Controllers\Site\ReportController;
|
||||||
|
use App\Models\PriceSyncLog;
|
||||||
use App\Models\Product;
|
use App\Models\Product;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
@@ -12,13 +13,15 @@ class LivePriceTickService
|
|||||||
{
|
{
|
||||||
private const LOCK_SECONDS = 25;
|
private const LOCK_SECONDS = 25;
|
||||||
|
|
||||||
public function tick(): void
|
public function tick(): ?PriceSyncLog
|
||||||
{
|
{
|
||||||
$lock = Cache::lock('prices:tick', self::LOCK_SECONDS);
|
$lock = Cache::lock('prices:tick', self::LOCK_SECONDS);
|
||||||
if (! $lock->get()) {
|
if (! $lock->get()) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$syncLog = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$config = app(ConfigService::class);
|
$config = app(ConfigService::class);
|
||||||
$sync = app(ProductPriceSyncService::class);
|
$sync = app(ProductPriceSyncService::class);
|
||||||
@@ -73,15 +76,22 @@ class LivePriceTickService
|
|||||||
->whereNotNull('reading_method')
|
->whereNotNull('reading_method')
|
||||||
->count();
|
->count();
|
||||||
|
|
||||||
app(PriceSyncLogService::class)->recordTick($source, $sourceUrl, $liquidGold, $activeProducts);
|
$syncLog = app(PriceSyncLogService::class)->recordTick($source, $sourceUrl, $liquidGold, $activeProducts);
|
||||||
|
|
||||||
if ($payload !== []) {
|
if ($payload !== []) {
|
||||||
app(PriceSnapshotService::class)->recordTick($payload);
|
app(PriceSnapshotService::class)->recordTick($payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
app(LivePriceCacheService::class)->markTick();
|
app(LivePriceCacheService::class)->markTick();
|
||||||
|
|
||||||
|
$broadcastPayload = $payload;
|
||||||
|
dispatch(function () use ($broadcastPayload) {
|
||||||
|
app(PriceBroadcastService::class)->broadcastAfterPriceUpdate($broadcastPayload);
|
||||||
|
})->afterResponse();
|
||||||
} finally {
|
} finally {
|
||||||
$lock->release();
|
$lock->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $syncLog;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Admin\ProductController;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
class PriceBroadcastService
|
||||||
|
{
|
||||||
|
private const LAST_HASH_KEY = 'ws_broadcast:last_hash';
|
||||||
|
|
||||||
|
private const LIST_HASH_KEY = 'ws_broadcast:list_hash';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed>|null $lastPayload
|
||||||
|
*/
|
||||||
|
public function broadcastAfterPriceUpdate(?array $lastPayload = null): void
|
||||||
|
{
|
||||||
|
$ws = app(WebsocketBroadcastService::class);
|
||||||
|
if (! $ws->isEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($lastPayload === null) {
|
||||||
|
$cache = app(LivePriceCacheService::class);
|
||||||
|
$lastPayload = $cache->get() ?? $cache->buildFromDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($lastPayload) && $lastPayload !== []) {
|
||||||
|
$this->broadcastLastIfChanged($lastPayload);
|
||||||
|
}
|
||||||
|
|
||||||
|
$listPayload = $this->buildProductListPayload();
|
||||||
|
if ($listPayload !== null) {
|
||||||
|
$this->broadcastListIfChanged($listPayload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $payload
|
||||||
|
*/
|
||||||
|
public function broadcastLastIfChanged(array $payload): void
|
||||||
|
{
|
||||||
|
$hash = md5(json_encode($payload, JSON_UNESCAPED_UNICODE));
|
||||||
|
if (Cache::get(self::LAST_HASH_KEY) === $hash) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cache::put(self::LAST_HASH_KEY, $hash, now()->addHour());
|
||||||
|
app(WebsocketBroadcastService::class)->sendEvent('last', $payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<int, mixed> $products
|
||||||
|
*/
|
||||||
|
public function broadcastListIfChanged(array $products): void
|
||||||
|
{
|
||||||
|
$hash = md5(json_encode($products, JSON_UNESCAPED_UNICODE));
|
||||||
|
if (Cache::get(self::LIST_HASH_KEY) === $hash) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cache::put(self::LIST_HASH_KEY, $hash, now()->addHour());
|
||||||
|
app(WebsocketBroadcastService::class)->sendEvent('list', ['data' => $products]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int, mixed>|null
|
||||||
|
*/
|
||||||
|
private function buildProductListPayload(): ?array
|
||||||
|
{
|
||||||
|
$request = Request::create('/customer/product/list', 'GET');
|
||||||
|
app()->instance('request', $request);
|
||||||
|
|
||||||
|
$response = app(ProductController::class)->listProduct();
|
||||||
|
$decoded = json_decode($response->getContent(), true);
|
||||||
|
|
||||||
|
return is_array($decoded) ? $decoded : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,9 +8,30 @@ use Carbon\Carbon;
|
|||||||
|
|
||||||
class PriceSnapshotService
|
class PriceSnapshotService
|
||||||
{
|
{
|
||||||
/** حداقل قیمت معتبر آبشده (ریال) — زیر این در نمودار نمایش داده نمیشود */
|
/** حداقل قیمت معتبر آبشده (ریال) — برای تاریخچه ذخیرهشده در DB */
|
||||||
public const MIN_VALID_CHART_PRICE_RIAL = 30_000_000;
|
public const MIN_VALID_CHART_PRICE_RIAL = 30_000_000;
|
||||||
|
|
||||||
|
/** حداقل پیشفرض نمودار Zaarbox — مس و کالاهای ارزانتر از آبشده */
|
||||||
|
public const MIN_VALID_ZAARBOX_CHART_PRICE_RIAL = 1_000_000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* آستانه حداقل قیمت نمودار تابلو — قابل تنظیم با chart_min_valid_price.
|
||||||
|
* پیشفرض Zaarbox پایینتر از آبشده است تا مس (~۲۲M) فیلتر نشود.
|
||||||
|
*/
|
||||||
|
public function minValidChartPriceRialForExternalFeed(): int
|
||||||
|
{
|
||||||
|
$configured = (int) app(ConfigService::class)->getString('chart_min_valid_price', '0');
|
||||||
|
if ($configured > 0) {
|
||||||
|
return $configured;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app(ConfigService::class)->usesZaarboxPricing()) {
|
||||||
|
return self::MIN_VALID_ZAARBOX_CHART_PRICE_RIAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::MIN_VALID_CHART_PRICE_RIAL;
|
||||||
|
}
|
||||||
|
|
||||||
/** هر تیک کران — ذخیره در prices برای نمودار (هرگز صفر ثبت نمیشود) */
|
/** هر تیک کران — ذخیره در prices برای نمودار (هرگز صفر ثبت نمیشود) */
|
||||||
public function recordTick(array $payload): void
|
public function recordTick(array $payload): void
|
||||||
{
|
{
|
||||||
@@ -23,6 +44,11 @@ class PriceSnapshotService
|
|||||||
$priceValue = (int) $last->price;
|
$priceValue = (int) $last->price;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$lastRecorded = Price::query()->orderByDesc('id')->first();
|
||||||
|
if ($lastRecorded && (int) $lastRecorded->price === $priceValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Price::create([
|
Price::create([
|
||||||
'price' => $priceValue,
|
'price' => $priceValue,
|
||||||
'date_at' => Carbon::now()->toDateString(),
|
'date_at' => Carbon::now()->toDateString(),
|
||||||
@@ -199,9 +225,10 @@ class PriceSnapshotService
|
|||||||
* @param array{DAILY?: array, WEEKLY?: array, MONTHLY?: array} $prices
|
* @param array{DAILY?: array, WEEKLY?: array, MONTHLY?: array} $prices
|
||||||
* @return array{DAILY: array, WEEKLY: array, MONTHLY: array}
|
* @return array{DAILY: array, WEEKLY: array, MONTHLY: array}
|
||||||
*/
|
*/
|
||||||
public function sanitizeChartPrices(array $prices): array
|
public function sanitizeChartPrices(array $prices, ?int $minRial = null): array
|
||||||
{
|
{
|
||||||
$minScaled = intdiv(self::MIN_VALID_CHART_PRICE_RIAL, 1000);
|
$minRial ??= self::MIN_VALID_CHART_PRICE_RIAL;
|
||||||
|
$minScaled = intdiv($minRial, 1000);
|
||||||
|
|
||||||
foreach (['DAILY', 'WEEKLY', 'MONTHLY'] as $period) {
|
foreach (['DAILY', 'WEEKLY', 'MONTHLY'] as $period) {
|
||||||
if (! isset($prices[$period]) || ! is_array($prices[$period])) {
|
if (! isset($prices[$period]) || ! is_array($prices[$period])) {
|
||||||
@@ -220,7 +247,7 @@ class PriceSnapshotService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$isValid = $value > self::MIN_VALID_CHART_PRICE_RIAL
|
$isValid = $value > $minRial
|
||||||
|| ($value > $minScaled && $value <= self::MIN_VALID_CHART_PRICE_RIAL);
|
|| ($value > $minScaled && $value <= self::MIN_VALID_CHART_PRICE_RIAL);
|
||||||
|
|
||||||
if ($isValid) {
|
if ($isValid) {
|
||||||
|
|||||||
@@ -57,10 +57,15 @@ class PriceSyncLogService
|
|||||||
return $log->load('items');
|
return $log->load('items');
|
||||||
}
|
}
|
||||||
|
|
||||||
/** هر تیک کران — حتی بدون تغییر قیمت محصول */
|
/** فقط وقتی قیمت مرجع آبشده نسبت به آخرین رکورد عوض شده باشد */
|
||||||
public function recordTick(string $source, ?string $sourceUrl, int $liquidGoldPrice, int $productsSynced): ?PriceSyncLog
|
public function recordTick(string $source, ?string $sourceUrl, int $liquidGoldPrice, int $productsSynced): ?PriceSyncLog
|
||||||
{
|
{
|
||||||
if (! $this->isAvailable()) {
|
if (! $this->isAvailable() || $liquidGoldPrice <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$last = PriceSyncLog::query()->orderByDesc('id')->first();
|
||||||
|
if ($last && (int) $last->liquid_gold_price === $liquidGoldPrice) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class WebsocketBroadcastService
|
||||||
|
{
|
||||||
|
private const DEFAULT_TRIGGER_PATH = '/prc/api/trigger-update';
|
||||||
|
|
||||||
|
public function triggerBaseUrl(): ?string
|
||||||
|
{
|
||||||
|
$fromEnv = trim((string) (env('WSS_LINK') ?: env('wssLink', '')));
|
||||||
|
if ($fromEnv !== '') {
|
||||||
|
return rtrim($fromEnv, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
$fromSetting = trim((string) app(ConfigService::class)->getString('wss_link', ''));
|
||||||
|
if ($fromSetting !== '') {
|
||||||
|
return rtrim($fromSetting, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function triggerPath(): string
|
||||||
|
{
|
||||||
|
$path = trim((string) app(ConfigService::class)->getString('wss_trigger_path', ''));
|
||||||
|
|
||||||
|
return $path !== '' ? $path : self::DEFAULT_TRIGGER_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isEnabled(): bool
|
||||||
|
{
|
||||||
|
if (! app(ConfigService::class)->getBool('wss_enabled', true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->triggerBaseUrl() !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $payload
|
||||||
|
*/
|
||||||
|
public function sendEvent(string $trigger, array $payload = []): void
|
||||||
|
{
|
||||||
|
if (! $this->isEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$baseUrl = $this->triggerBaseUrl();
|
||||||
|
if ($baseUrl === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$body = array_merge($payload, ['trigger' => $trigger]);
|
||||||
|
$host = $this->resolveHost();
|
||||||
|
if ($host !== '') {
|
||||||
|
$body['host'] = $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
$url = $baseUrl.$this->triggerPath();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Http::timeout(3)
|
||||||
|
->withHeaders(['Content-Type' => 'application/json'])
|
||||||
|
->withOptions([
|
||||||
|
'curl' => [
|
||||||
|
CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4,
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->post($url, $body);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::warning('Websocket broadcast failed', [
|
||||||
|
'trigger' => $trigger,
|
||||||
|
'url' => $url,
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function resolveHost(): string
|
||||||
|
{
|
||||||
|
$config = app(ConfigService::class);
|
||||||
|
$nameEn = trim((string) $config->getString('name_en', ''));
|
||||||
|
if ($nameEn !== '') {
|
||||||
|
return $nameEn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! app()->runningInConsole() && request()) {
|
||||||
|
return trim((string) request()->getHost());
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -90,20 +90,23 @@ class ZaarboxPriceService
|
|||||||
*/
|
*/
|
||||||
public function getAllPrices(): array
|
public function getAllPrices(): array
|
||||||
{
|
{
|
||||||
|
$snapshot = app(PriceSnapshotService::class);
|
||||||
|
|
||||||
if ($this->config->getBool('chart_use_local_data', false)) {
|
if ($this->config->getBool('chart_use_local_data', false)) {
|
||||||
$local = app(PriceSnapshotService::class)->chartFromDatabase();
|
$local = $snapshot->chartFromDatabase();
|
||||||
if (! empty($local['DAILY'])) {
|
if (! empty($local['DAILY'])) {
|
||||||
return app(PriceSnapshotService::class)->sanitizeChartPrices($local);
|
return $snapshot->sanitizeChartPrices($local);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$minRial = $snapshot->minValidChartPriceRialForExternalFeed();
|
||||||
$cached = Cache::get($this->cacheKey());
|
$cached = Cache::get($this->cacheKey());
|
||||||
|
|
||||||
if (is_array($cached) && isset($cached['prices'])) {
|
if (is_array($cached) && isset($cached['prices'])) {
|
||||||
return app(PriceSnapshotService::class)->sanitizeChartPrices($cached['prices']);
|
return $snapshot->sanitizeChartPrices($cached['prices'], $minRial);
|
||||||
}
|
}
|
||||||
|
|
||||||
return app(PriceSnapshotService::class)->sanitizeChartPrices($this->refreshCache());
|
return $snapshot->sanitizeChartPrices($this->refreshCache(), $minRial);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -483,6 +486,8 @@ class ZaarboxPriceService
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$minRial = app(PriceSnapshotService::class)->minValidChartPriceRialForExternalFeed();
|
||||||
|
|
||||||
foreach (self::PERIODS as $key => $config) {
|
foreach (self::PERIODS as $key => $config) {
|
||||||
$response = $responses[$key] ?? null;
|
$response = $responses[$key] ?? null;
|
||||||
$start = $now->copy()->subHours($config['hours']);
|
$start = $now->copy()->subHours($config['hours']);
|
||||||
@@ -501,7 +506,8 @@ class ZaarboxPriceService
|
|||||||
$history,
|
$history,
|
||||||
$this->toMillis($start),
|
$this->toMillis($start),
|
||||||
$this->toMillis($now),
|
$this->toMillis($now),
|
||||||
$config['intervalMs']
|
$config['intervalMs'],
|
||||||
|
$minRial
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,8 +516,14 @@ class ZaarboxPriceService
|
|||||||
|
|
||||||
private function historyUrl(Carbon $start, Carbon $end): string
|
private function historyUrl(Carbon $start, Carbon $end): string
|
||||||
{
|
{
|
||||||
|
$base = rtrim($this->apiUrl(), '/');
|
||||||
|
if (str_ends_with($base, '/free')) {
|
||||||
|
$base = substr($base, 0, -strlen('/free'));
|
||||||
|
}
|
||||||
|
|
||||||
return sprintf(
|
return sprintf(
|
||||||
'https://price.zaarbox.ir/api/history/%s?start=%s&end=%s&limit=1000000000',
|
'%s/history/%s?start=%s&end=%s&limit=1000000000',
|
||||||
|
$base,
|
||||||
$this->historyId(),
|
$this->historyId(),
|
||||||
$this->toIso8601($start),
|
$this->toIso8601($start),
|
||||||
$this->toIso8601($end)
|
$this->toIso8601($end)
|
||||||
@@ -532,7 +544,7 @@ class ZaarboxPriceService
|
|||||||
* @param array<int, array<string, mixed>> $history
|
* @param array<int, array<string, mixed>> $history
|
||||||
* @return array<int, array{timestamp: int, value: int}>
|
* @return array<int, array{timestamp: int, value: int}>
|
||||||
*/
|
*/
|
||||||
private function mapHistory(array $history, int $startMs, int $endMs, int $intervalMs): array
|
private function mapHistory(array $history, int $startMs, int $endMs, int $intervalMs, int $minRial): array
|
||||||
{
|
{
|
||||||
$points = [];
|
$points = [];
|
||||||
|
|
||||||
@@ -566,7 +578,7 @@ class ZaarboxPriceService
|
|||||||
|
|
||||||
for ($bucket = $firstBucket; $bucket <= $endMs; $bucket += $intervalMs) {
|
for ($bucket = $firstBucket; $bucket <= $endMs; $bucket += $intervalMs) {
|
||||||
while ($index < $count && $points[$index]['timestamp'] <= $bucket) {
|
while ($index < $count && $points[$index]['timestamp'] <= $bucket) {
|
||||||
if ($points[$index]['price'] > PriceSnapshotService::MIN_VALID_CHART_PRICE_RIAL) {
|
if ($points[$index]['price'] > $minRial) {
|
||||||
$lastPoint = $points[$index];
|
$lastPoint = $points[$index];
|
||||||
}
|
}
|
||||||
$index++;
|
$index++;
|
||||||
|
|||||||
@@ -193,6 +193,23 @@ INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `de
|
|||||||
SELECT 'external_order_api_enabled', 'true', 'فعالسازی API سفارش خارجی (get_wallet / order)', 1, 1, 'true + node_client_id + node_shared_secret'
|
SELECT 'external_order_api_enabled', 'true', 'فعالسازی API سفارش خارجی (get_wallet / order)', 1, 1, 'true + node_client_id + node_shared_secret'
|
||||||
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'external_order_api_enabled' LIMIT 1);
|
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'external_order_api_enabled' LIMIT 1);
|
||||||
|
|
||||||
|
-- WebSocket قیمت (برادکست last/list)
|
||||||
|
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||||
|
SELECT 'wss_enabled', 'true', 'قیمت لحظهای از WebSocket', 1, 1, 'روشن = کلاینت از /prc/ws استفاده میکند؛ خاموش = polling', 'switch'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'wss_enabled' LIMIT 1);
|
||||||
|
|
||||||
|
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||||
|
SELECT 'wss_path', '/prc/ws', 'مسیر WebSocket قیمت', 1, 1, 'مسیر پروکسی لایتاسپید — مثال: /prc/ws', 'text'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'wss_path' LIMIT 1);
|
||||||
|
|
||||||
|
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||||
|
SELECT 'wss_link', '', 'آدرس trigger وبسوکت (سرور)', 1, 1, 'مثال: http://localhost:2090 — اگر خالی باشد از env WSS_LINK/wssLink', 'text'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'wss_link' LIMIT 1);
|
||||||
|
|
||||||
|
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||||
|
SELECT 'wss_trigger_path', '/prc/api/trigger-update', 'مسیر trigger برادکست', 1, 1, 'POST برای ارسال last/list به سرویس ws', 'text'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'wss_trigger_path' LIMIT 1);
|
||||||
|
|
||||||
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`)
|
||||||
SELECT 'montify_name', '', 'نام فروشگاه در درگاه', 1, 1, NULL, 'text'
|
SELECT 'montify_name', '', 'نام فروشگاه در درگاه', 1, 1, NULL, 'text'
|
||||||
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montify_name' LIMIT 1);
|
WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montify_name' LIMIT 1);
|
||||||
@@ -513,6 +530,18 @@ WHERE `key` = 'node_shared_secret';
|
|||||||
UPDATE `settings` SET `title_fa` = COALESCE(NULLIF(TRIM(`title_fa`), ''), 'API سفارش خارجی')
|
UPDATE `settings` SET `title_fa` = COALESCE(NULLIF(TRIM(`title_fa`), ''), 'API سفارش خارجی')
|
||||||
WHERE `key` = 'external_order_api_enabled';
|
WHERE `key` = 'external_order_api_enabled';
|
||||||
|
|
||||||
|
UPDATE `settings` SET `title_fa` = COALESCE(NULLIF(TRIM(`title_fa`), ''), 'قیمت لحظهای از WebSocket')
|
||||||
|
WHERE `key` = 'wss_enabled';
|
||||||
|
|
||||||
|
UPDATE `settings` SET `title_fa` = COALESCE(NULLIF(TRIM(`title_fa`), ''), 'مسیر WebSocket قیمت')
|
||||||
|
WHERE `key` = 'wss_path';
|
||||||
|
|
||||||
|
UPDATE `settings` SET `title_fa` = COALESCE(NULLIF(TRIM(`title_fa`), ''), 'آدرس trigger وبسوکت')
|
||||||
|
WHERE `key` = 'wss_link';
|
||||||
|
|
||||||
|
UPDATE `settings` SET `title_fa` = COALESCE(NULLIF(TRIM(`title_fa`), ''), 'مسیر trigger برادکست')
|
||||||
|
WHERE `key` = 'wss_trigger_path';
|
||||||
|
|
||||||
UPDATE `settings` SET `title_fa` = COALESCE(NULLIF(TRIM(`title_fa`), ''), 'بسته بودن همکار')
|
UPDATE `settings` SET `title_fa` = COALESCE(NULLIF(TRIM(`title_fa`), ''), 'بسته بودن همکار')
|
||||||
WHERE `key` = 'montzar_closed';
|
WHERE `key` = 'montzar_closed';
|
||||||
|
|
||||||
@@ -580,7 +609,7 @@ UPDATE `settings` SET `type` = 'switch' WHERE `key` IN (
|
|||||||
'send_to_tahesab', 'profit_gold_api_enabled', 'payping_round_toman', 'payping_strict_failed_withdraw',
|
'send_to_tahesab', 'profit_gold_api_enabled', 'payping_round_toman', 'payping_strict_failed_withdraw',
|
||||||
'has_page_earn_money', 'charge_notif', 'group_sms',
|
'has_page_earn_money', 'charge_notif', 'group_sms',
|
||||||
'montzar_closed', 'montzar_open_buy', 'montzar_open_sell',
|
'montzar_closed', 'montzar_open_buy', 'montzar_open_sell',
|
||||||
'payping_fin_sync_enabled', 'external_order_api_enabled', 'is_store',
|
'payping_fin_sync_enabled', 'external_order_api_enabled', 'is_store', 'wss_enabled',
|
||||||
'register_referral_sms_to_referrer', 'register_referral_bonus_message', 'price_from_nasiryan'
|
'register_referral_sms_to_referrer', 'register_referral_bonus_message', 'price_from_nasiryan'
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -603,6 +632,7 @@ UPDATE `settings` SET `type` = 'text' WHERE `key` IN (
|
|||||||
'payping_start_url', 'payping_verify_url', 'payping_gateway_items', 'payping_fin_url',
|
'payping_start_url', 'payping_verify_url', 'payping_gateway_items', 'payping_fin_url',
|
||||||
'payping_fin_token', 'payping_gateway_item', 'tahesab_host', 'tahesab_token', 'tahesab_db',
|
'payping_fin_token', 'payping_gateway_item', 'tahesab_host', 'tahesab_token', 'tahesab_db',
|
||||||
'node_client_id', 'node_shared_secret',
|
'node_client_id', 'node_shared_secret',
|
||||||
|
'wss_path', 'wss_link', 'wss_trigger_path',
|
||||||
'tahesab_bank_account', 'telegram_bot_token', 'bale_bot_token', 'support_messenger_id', 'support_messenger_text', 'referral_limit',
|
'tahesab_bank_account', 'telegram_bot_token', 'bale_bot_token', 'support_messenger_id', 'support_messenger_text', 'referral_limit',
|
||||||
'referral_bonus_amount', 'balance_report_exclude_customer_id', 'metal_type', 'montify_name', 'price_extreme_date',
|
'referral_bonus_amount', 'balance_report_exclude_customer_id', 'metal_type', 'montify_name', 'price_extreme_date',
|
||||||
'default_vc_number', 'liman_token'
|
'default_vc_number', 'liman_token'
|
||||||
@@ -929,6 +959,10 @@ UPDATE `settings` SET `description` = 'پاسخ JSON آخرین سفارش ار
|
|||||||
UPDATE `settings` SET `description` = 'شناسه کلاینت برای API سفارش خارجی (هدر X-Client-Id). باید با مقدار تنظیمشده در سرویس مقصد یکسان باشد.' WHERE `key` = 'node_client_id';
|
UPDATE `settings` SET `description` = 'شناسه کلاینت برای API سفارش خارجی (هدر X-Client-Id). باید با مقدار تنظیمشده در سرویس مقصد یکسان باشد.' WHERE `key` = 'node_client_id';
|
||||||
UPDATE `settings` SET `description` = 'کلید مشترک امضای HMAC برای احراز درخواستهای API سفارش خارجی. محرمانه نگه دارید.' WHERE `key` = 'node_shared_secret';
|
UPDATE `settings` SET `description` = 'کلید مشترک امضای HMAC برای احراز درخواستهای API سفارش خارجی. محرمانه نگه دارید.' WHERE `key` = 'node_shared_secret';
|
||||||
UPDATE `settings` SET `description` = 'روشن: API دریافت کیف پول و ثبت سفارش از سرویس فروشگاه خارجی فعال است. نیاز به node_client_id و node_shared_secret دارد.' WHERE `key` = 'external_order_api_enabled';
|
UPDATE `settings` SET `description` = 'روشن: API دریافت کیف پول و ثبت سفارش از سرویس فروشگاه خارجی فعال است. نیاز به node_client_id و node_shared_secret دارد.' WHERE `key` = 'external_order_api_enabled';
|
||||||
|
UPDATE `settings` SET `description` = 'روشن: اپ کاربر بهجای polling مکرر /api/price/last و /customer/product/list از WebSocket استفاده میکند. نیاز به سرویس ws و پروکسی لایتاسپید دارد.' WHERE `key` = 'wss_enabled';
|
||||||
|
UPDATE `settings` SET `description` = 'مسیر WebSocket روی دامنه اپ — معمولاً /prc/ws (پروکسی به پورت داخلی ws).' WHERE `key` = 'wss_path';
|
||||||
|
UPDATE `settings` SET `description` = 'آدرس HTTP سرویس ws برای trigger برادکست از سرور لاراول — مثال: http://localhost:2090. اولویت با env WSS_LINK/wssLink است.' WHERE `key` = 'wss_link';
|
||||||
|
UPDATE `settings` SET `description` = 'مسیر POST برای ارسال رویداد last/list به سرویس ws — پیشفرض: /prc/api/trigger-update' WHERE `key` = 'wss_trigger_path';
|
||||||
UPDATE `settings` SET `description` = 'نام فروشگاه که در درگاه پرداخت و رسید بانکی به کاربر نمایش داده میشود.' WHERE `key` = 'montify_name';
|
UPDATE `settings` SET `description` = 'نام فروشگاه که در درگاه پرداخت و رسید بانکی به کاربر نمایش داده میشود.' WHERE `key` = 'montify_name';
|
||||||
|
|
||||||
UPDATE `settings` SET `description` = 'روشن: پس از واریز/برداشت، تراکنش به سامانه حسابداری fin همگامسازی میشود.' WHERE `key` = 'payping_fin_sync_enabled';
|
UPDATE `settings` SET `description` = 'روشن: پس از واریز/برداشت، تراکنش به سامانه حسابداری fin همگامسازی میشود.' WHERE `key` = 'payping_fin_sync_enabled';
|
||||||
|
|||||||
Reference in New Issue
Block a user