2026-08-18 16:10:37 +03:30
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Services;
|
|
|
|
|
|
2026-08-22 18:06:35 +03:30
|
|
|
use Illuminate\Support\Facades\Cache;
|
|
|
|
|
|
2026-08-18 16:10:37 +03:30
|
|
|
/**
|
2026-08-19 22:02:10 +03:30
|
|
|
* تنظیمات پیشرفته سوپرادمین برای ConfigService و get_settings.
|
2026-08-22 18:06:35 +03:30
|
|
|
* در هر درخواست یک بار fetch میشود؛ آخرین پاسخ سالم برای قطعی لحظهای کش میماند.
|
2026-08-18 16:10:37 +03:30
|
|
|
*/
|
|
|
|
|
class SuperAdminProgramSettingsOverlay
|
|
|
|
|
{
|
2026-08-22 18:06:35 +03:30
|
|
|
/**
|
2026-08-25 03:01:07 +03:30
|
|
|
* آخرین پاسخ سالم سوپرادمین — فقط کلیدهای دسترسی (SUPERADMIN_OWNED_KEYS).
|
|
|
|
|
* پاسخ remote ممکن است کلیدهای دیگری هم داشته باشد؛ آنها ذخیره و اعمال نمیشوند.
|
2026-08-22 18:06:35 +03:30
|
|
|
*/
|
|
|
|
|
private const LAST_GOOD_CACHE_KEY = 'superadmin:program_settings:last_good';
|
|
|
|
|
|
|
|
|
|
private const LAST_GOOD_CACHE_MINUTES = 1440;
|
|
|
|
|
|
2026-08-23 22:34:18 +03:30
|
|
|
/**
|
2026-08-25 03:01:07 +03:30
|
|
|
* فقط این ۱۴ دسترسی از سوپرادمین خوانده میشود.
|
|
|
|
|
* هر کلید دیگری (قیمت، ساعت کاری، برندینگ، …) مال پنل خانگی است.
|
2026-08-23 22:34:18 +03:30
|
|
|
*/
|
|
|
|
|
public const SUPERADMIN_OWNED_KEYS = [
|
|
|
|
|
'customer_direct_sms',
|
|
|
|
|
'customer_special_pricing',
|
2026-08-24 16:15:52 +03:30
|
|
|
'enable_automation',
|
2026-08-23 22:34:18 +03:30
|
|
|
'enable_conditional_order',
|
|
|
|
|
'enable_customer_level_profit',
|
|
|
|
|
'enable_product_funds',
|
|
|
|
|
'group_sms',
|
|
|
|
|
'has_page_earn_money',
|
|
|
|
|
'has_page_gift_card',
|
|
|
|
|
'has_page_notification',
|
|
|
|
|
'has_page_price_trigger',
|
|
|
|
|
'jibit_enabled',
|
|
|
|
|
'permision__enabled',
|
|
|
|
|
'show_to_users_chat',
|
|
|
|
|
];
|
|
|
|
|
|
2026-08-25 14:19:22 +03:30
|
|
|
/**
|
|
|
|
|
* دسترسیهای سوپرادمین که منو/مسیر اپ کاربر را کنترل میکنند.
|
|
|
|
|
* باید همیشه در api/get_settings مشتری برسند (show_to_user).
|
|
|
|
|
*/
|
|
|
|
|
public const CLIENT_FEATURE_KEYS = [
|
|
|
|
|
'enable_conditional_order',
|
|
|
|
|
'enable_product_funds',
|
|
|
|
|
'has_page_earn_money',
|
|
|
|
|
'has_page_gift_card',
|
|
|
|
|
'has_page_notification',
|
|
|
|
|
'has_page_price_trigger',
|
|
|
|
|
'jibit_enabled',
|
|
|
|
|
'permision__enabled',
|
|
|
|
|
'show_to_users_chat',
|
|
|
|
|
];
|
|
|
|
|
|
2026-08-24 16:15:52 +03:30
|
|
|
/** کلیدهایی که هنوز صفحهٔ اختصاصی ادمین خانگی مالکشان است — از overlay دور انداخته میشوند */
|
2026-08-18 16:10:37 +03:30
|
|
|
public const LOCAL_OWNED_KEYS = [
|
|
|
|
|
'tracking_systems',
|
|
|
|
|
];
|
|
|
|
|
|
2026-08-19 19:55:29 +03:30
|
|
|
/** لاگ/قیمت لحظهای همکار — can_edit=1 در DB؛ کد مستقیم UpdateConfig میزند، نه سوپرادمین */
|
2026-08-18 16:10:37 +03:30
|
|
|
public const RUNTIME_KEYS = [
|
|
|
|
|
'montzar_last_product_log',
|
|
|
|
|
'montzar_order_response',
|
|
|
|
|
'montzar_live_price',
|
|
|
|
|
'montzar_mazaneh',
|
|
|
|
|
'montzar_diff',
|
|
|
|
|
'montzar_truly_buy_mazaneh',
|
|
|
|
|
'montzar_truly_sell_mazaneh',
|
|
|
|
|
'montzar_token',
|
|
|
|
|
'montzar_closed',
|
|
|
|
|
'up_price',
|
|
|
|
|
'down_price',
|
|
|
|
|
'price_extreme_date',
|
|
|
|
|
'do_send_liman',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
/** @var array<string, array<string, mixed>>|null */
|
|
|
|
|
private static ?array $memo = null;
|
|
|
|
|
|
|
|
|
|
private static bool $loaded = false;
|
|
|
|
|
|
2026-08-24 21:22:44 +03:30
|
|
|
/** پاک کردن کش last-good و memo درخواست — برای دکمهٔ همگامسازی تنظیمات پیشرفته */
|
|
|
|
|
public function flushCache(): void
|
|
|
|
|
{
|
|
|
|
|
self::$memo = null;
|
|
|
|
|
self::$loaded = false;
|
|
|
|
|
try {
|
|
|
|
|
Cache::forget(self::LAST_GOOD_CACHE_KEY);
|
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
// کش در دسترس نیست
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* کش را پاک میکند و دوباره از سوپرادمین میخواند.
|
|
|
|
|
*
|
2026-08-24 21:46:13 +03:30
|
|
|
* @return array{ok: bool, count: int, items: array<string, string>, source: string}
|
2026-08-24 21:22:44 +03:30
|
|
|
*/
|
|
|
|
|
public function refreshFromSuperAdmin(): array
|
|
|
|
|
{
|
|
|
|
|
$this->flushCache();
|
|
|
|
|
$map = $this->catalogMap();
|
|
|
|
|
if ($map === null) {
|
|
|
|
|
return [
|
|
|
|
|
'ok' => false,
|
|
|
|
|
'count' => 0,
|
2026-08-24 21:46:13 +03:30
|
|
|
'items' => [],
|
2026-08-24 21:22:44 +03:30
|
|
|
'source' => 'unavailable',
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-24 21:46:13 +03:30
|
|
|
$items = [];
|
|
|
|
|
foreach ($map as $key => $row) {
|
|
|
|
|
if (! is_array($row)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$value = $row['value'] ?? null;
|
|
|
|
|
if ($value === null || $value === '') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$items[(string) $key] = (string) $value;
|
|
|
|
|
}
|
2026-08-24 21:22:44 +03:30
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'ok' => true,
|
2026-08-24 21:46:13 +03:30
|
|
|
'count' => count($items),
|
|
|
|
|
'items' => $items,
|
2026-08-24 21:22:44 +03:30
|
|
|
'source' => 'superadmin',
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-18 16:10:37 +03:30
|
|
|
/**
|
2026-08-19 22:02:10 +03:30
|
|
|
* همهٔ کلیدهای سوپرادمین (حتی value خالی). null = سوپرادمین در دسترس نبود.
|
|
|
|
|
*
|
|
|
|
|
* @return array<string, array<string, mixed>>|null
|
|
|
|
|
*/
|
|
|
|
|
public function catalogMap(): ?array
|
|
|
|
|
{
|
|
|
|
|
return $this->load();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* فقط کلیدهایی که سوپرادمین مقدار غیرخالی دارد — برای ConfigService.
|
|
|
|
|
*
|
|
|
|
|
* @return array<string, array<string, mixed>>|null
|
2026-08-18 16:10:37 +03:30
|
|
|
*/
|
|
|
|
|
public function map(): ?array
|
2026-08-19 22:02:10 +03:30
|
|
|
{
|
|
|
|
|
$full = $this->load();
|
|
|
|
|
if ($full === null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$withValue = [];
|
|
|
|
|
foreach ($full as $key => $row) {
|
|
|
|
|
$value = is_array($row) ? ($row['value'] ?? null) : null;
|
|
|
|
|
if ($value === null || $value === '') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$withValue[$key] = $row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $withValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return array<string, array<string, mixed>>|null
|
|
|
|
|
*/
|
|
|
|
|
private function load(): ?array
|
2026-08-18 16:10:37 +03:30
|
|
|
{
|
|
|
|
|
if (self::$loaded) {
|
|
|
|
|
return self::$memo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self::$loaded = true;
|
|
|
|
|
|
|
|
|
|
$items = app(SuperAdminProgramSettingsClient::class)->fetchItems();
|
2026-08-22 18:06:35 +03:30
|
|
|
|
|
|
|
|
if ($items !== null) {
|
|
|
|
|
$this->rememberLastGood($items);
|
|
|
|
|
} else {
|
|
|
|
|
$items = $this->lastGoodItems();
|
2026-08-24 21:22:44 +03:30
|
|
|
if ($items !== null) {
|
|
|
|
|
\Illuminate\Support\Facades\Log::warning('SuperAdmin program-settings using last-good cache', [
|
|
|
|
|
'count' => count($items),
|
|
|
|
|
]);
|
|
|
|
|
}
|
2026-08-22 18:06:35 +03:30
|
|
|
}
|
|
|
|
|
|
2026-08-18 16:10:37 +03:30
|
|
|
if ($items === null) {
|
|
|
|
|
self::$memo = null;
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-25 03:01:07 +03:30
|
|
|
$map = self::ownedItemsMap($items);
|
|
|
|
|
self::$memo = $map;
|
|
|
|
|
|
|
|
|
|
return $map;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* فقط کلیدهای دسترسی سوپرادمین — بقیهٔ پاسخ remote دور ریخته میشود.
|
|
|
|
|
*
|
|
|
|
|
* @param array<int, array<string, mixed>> $items
|
|
|
|
|
* @return array<string, array<string, mixed>>
|
|
|
|
|
*/
|
|
|
|
|
public static function ownedItemsMap(array $items): array
|
|
|
|
|
{
|
2026-08-18 16:10:37 +03:30
|
|
|
$map = [];
|
|
|
|
|
foreach ($items as $row) {
|
|
|
|
|
if (! is_array($row)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$key = trim((string) ($row['key'] ?? ''));
|
2026-08-25 03:01:07 +03:30
|
|
|
if ($key === '' || ! self::isSuperAdminOwned($key)) {
|
2026-08-18 16:10:37 +03:30
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$map[$key] = $row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $map;
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-22 18:06:35 +03:30
|
|
|
/** @param array<int, array<string, mixed>> $items */
|
|
|
|
|
private function rememberLastGood(array $items): void
|
|
|
|
|
{
|
2026-08-25 03:01:07 +03:30
|
|
|
$owned = array_values(self::ownedItemsMap($items));
|
|
|
|
|
if ($owned === []) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-22 18:06:35 +03:30
|
|
|
try {
|
|
|
|
|
Cache::put(
|
|
|
|
|
self::LAST_GOOD_CACHE_KEY,
|
2026-08-25 03:01:07 +03:30
|
|
|
$owned,
|
2026-08-22 18:06:35 +03:30
|
|
|
now()->addMinutes(self::LAST_GOOD_CACHE_MINUTES)
|
|
|
|
|
);
|
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
// کش در دسترس نیست — همان رفتار قبلی
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return array<int, array<string, mixed>>|null
|
|
|
|
|
*/
|
|
|
|
|
private function lastGoodItems(): ?array
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
$cached = Cache::get(self::LAST_GOOD_CACHE_KEY);
|
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return is_array($cached) && $cached !== [] ? $cached : null;
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-18 16:10:37 +03:30
|
|
|
/**
|
|
|
|
|
* @return array<int, array<string, mixed>>
|
|
|
|
|
*/
|
|
|
|
|
public function items(): array
|
|
|
|
|
{
|
|
|
|
|
$map = $this->map();
|
|
|
|
|
if ($map === null || $map === []) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return array_values($map);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return array<string, mixed>|null
|
|
|
|
|
*/
|
|
|
|
|
public function row(string $key): ?array
|
|
|
|
|
{
|
|
|
|
|
$map = $this->map();
|
|
|
|
|
if ($map === null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
$key = trim($key);
|
2026-08-25 03:01:07 +03:30
|
|
|
if ($key === '' || ! self::isSuperAdminOwned($key) || ! array_key_exists($key, $map)) {
|
2026-08-18 16:10:37 +03:30
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $map[$key];
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-18 18:52:29 +03:30
|
|
|
public static function isRuntime(string $key): bool
|
|
|
|
|
{
|
|
|
|
|
return in_array($key, self::RUNTIME_KEYS, true);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-25 03:01:07 +03:30
|
|
|
/**
|
|
|
|
|
* کلیدهای صفحهٔ «ساعت کاری» هم مالکشان پنل خانگی است — اگر از overlay خوانده شوند
|
|
|
|
|
* مقدار ذخیرهشده در پنل هیچوقت روی قیمت کاربر اعمال نمیشود.
|
|
|
|
|
*/
|
2026-08-18 16:10:37 +03:30
|
|
|
public static function isLocalOwned(string $key): bool
|
|
|
|
|
{
|
2026-08-25 03:01:07 +03:30
|
|
|
return in_array($key, self::LOCAL_OWNED_KEYS, true)
|
|
|
|
|
|| in_array($key, ConfigService::TRADING_HOURS_CLIENT_KEYS, true);
|
2026-08-18 16:10:37 +03:30
|
|
|
}
|
2026-08-23 22:34:18 +03:30
|
|
|
|
|
|
|
|
public static function isSuperAdminOwned(string $key): bool
|
|
|
|
|
{
|
|
|
|
|
return in_array(trim($key), self::SUPERADMIN_OWNED_KEYS, true);
|
|
|
|
|
}
|
2026-08-25 14:19:22 +03:30
|
|
|
|
|
|
|
|
public static function isClientFeatureKey(string $key): bool
|
|
|
|
|
{
|
|
|
|
|
return in_array(trim($key), self::CLIENT_FEATURE_KEYS, true);
|
|
|
|
|
}
|
2026-08-18 16:10:37 +03:30
|
|
|
}
|