Files
boursecu_server/app/Services/SuperAdminProgramSettingsOverlay.php
T
mohmdsdgandCursor 995f6984ed feat(automations): add run-now test endpoint and sends log table
Enable immediate automation test runs without waiting for the scheduled hour, expose send history for debugging, and return all SuperAdmin overlay keys on refresh instead of only enable_automation.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-24 21:46:13 +03:30

280 lines
8.3 KiB
PHP

<?php
namespace App\Services;
use Illuminate\Support\Facades\Cache;
/**
* تنظیمات پیشرفته سوپرادمین برای ConfigService و get_settings.
* در هر درخواست یک بار fetch می‌شود؛ آخرین پاسخ سالم برای قطعی لحظه‌ای کش می‌ماند.
*/
class SuperAdminProgramSettingsOverlay
{
/**
* آخرین پاسخ سالم سوپرادمین.
*
* کلیدهای can_edit=0 مالکشان سوپرادمین است و کپی‌شان در جدول settings لوکال
* ممکن است ماندهٔ deploy باشد. بدون این کش، یک timeout لحظه‌ای باعث می‌شود
* ConfigService به آن کپی سقوط کند — مثلاً price_provider از zaarbox به montzar
* می‌پرید و مظنهٔ قدیمی montzar_live_price به‌جای قیمت زنده نمایش داده می‌شد.
*/
private const LAST_GOOD_CACHE_KEY = 'superadmin:program_settings:last_good';
private const LAST_GOOD_CACHE_MINUTES = 1440;
/**
* کلیدهایی که فقط سوپرادمین ویرایش می‌کند (پنل خانگی 403).
* این لیست فیلتر overlay نیست — برای load از blacklist زیر استفاده می‌شود
* تا مثلاً price_provider از سوپرادمین/کش last-good حذف نشود.
*/
public const SUPERADMIN_OWNED_KEYS = [
'customer_direct_sms',
'customer_special_pricing',
'enable_automation',
'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',
];
/** کلیدهایی که هنوز صفحهٔ اختصاصی ادمین خانگی مالک‌شان است — از overlay دور انداخته می‌شوند */
public const LOCAL_OWNED_KEYS = [
'tracking_systems',
];
/** لاگ/قیمت لحظه‌ای همکار — can_edit=1 در DB؛ کد مستقیم UpdateConfig می‌زند، نه سوپرادمین */
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;
/** پاک کردن کش last-good و memo درخواست — برای دکمهٔ همگام‌سازی تنظیمات پیشرفته */
public function flushCache(): void
{
self::$memo = null;
self::$loaded = false;
try {
Cache::forget(self::LAST_GOOD_CACHE_KEY);
} catch (\Throwable $e) {
// کش در دسترس نیست
}
}
/**
* کش را پاک می‌کند و دوباره از سوپرادمین می‌خواند.
*
* @return array{ok: bool, count: int, items: array<string, string>, source: string}
*/
public function refreshFromSuperAdmin(): array
{
$this->flushCache();
$map = $this->catalogMap();
if ($map === null) {
return [
'ok' => false,
'count' => 0,
'items' => [],
'source' => 'unavailable',
];
}
$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;
}
return [
'ok' => true,
'count' => count($items),
'items' => $items,
'source' => 'superadmin',
];
}
/**
* همهٔ کلیدهای سوپرادمین (حتی value خالی). null = سوپرادمین در دسترس نبود.
*
* @return array<string, array<string, mixed>>|null
*/
public function catalogMap(): ?array
{
return $this->load();
}
/**
* فقط کلیدهایی که سوپرادمین مقدار غیرخالی دارد — برای ConfigService.
*
* @return array<string, array<string, mixed>>|null
*/
public function map(): ?array
{
$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
{
if (self::$loaded) {
return self::$memo;
}
self::$loaded = true;
$items = app(SuperAdminProgramSettingsClient::class)->fetchItems();
if ($items !== null) {
$this->rememberLastGood($items);
} else {
$items = $this->lastGoodItems();
if ($items !== null) {
\Illuminate\Support\Facades\Log::warning('SuperAdmin program-settings using last-good cache', [
'count' => count($items),
]);
}
}
if ($items === null) {
self::$memo = null;
return null;
}
$map = [];
foreach ($items as $row) {
if (! is_array($row)) {
continue;
}
$key = trim((string) ($row['key'] ?? ''));
// blacklist: فقط کلیدهای لوکال/runtime را دور بریز — نه whitelist قابلیت‌ها
// (whitelist قبلی price_provider را حذف می‌کرد و با قطعی SA به montzar لوکال می‌پرید)
if ($key === '' || self::isLocalOwned($key) || self::isRuntime($key)) {
continue;
}
$map[$key] = $row;
}
self::$memo = $map;
return $map;
}
/** @param array<int, array<string, mixed>> $items */
private function rememberLastGood(array $items): void
{
try {
Cache::put(
self::LAST_GOOD_CACHE_KEY,
$items,
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;
}
/**
* @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);
if ($key === '' || ! array_key_exists($key, $map)) {
return null;
}
return $map[$key];
}
public static function isRuntime(string $key): bool
{
return in_array($key, self::RUNTIME_KEYS, true);
}
public static function isLocalOwned(string $key): bool
{
return in_array($key, self::LOCAL_OWNED_KEYS, true);
}
public static function isSuperAdminOwned(string $key): bool
{
return in_array(trim($key), self::SUPERADMIN_OWNED_KEYS, true);
}
}