/
itp_practice
/
itp_backend
Обзор
Документация
Войти
/
itp_practice
/
itp_backend
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
master
app/Services/TapStatusSyncService.php
256 строк
9 KB
vivanenko
run taps
29 май 2025, 21:05
29 май 2025, 21:05
b91be9d
Код
Авторство
О чём код?
<?php namespace App\Services; use App\Models\Tap; use App\Enums\TapStatus; use App\Services\TapAppService; use Illuminate\Support\Facades\Log; use Illuminate\Database\Eloquent\Collection; /** * Сервис для массовой синхронизации статусов тапов * * Следует принципам SOLID: * - Single Responsibility: Только массовая синхронизация статусов * - Dependency Inversion: Зависит от TapAppService абстракции * - Open/Closed: Легко расширяется новыми стратегиями синхронизации */ class TapStatusSyncService { public function __construct( private readonly TapAppService $tapAppService ) {} /** * Business Logic Layer - массовая синхронизация всех активных тапов */ public function syncActiveTaps(): array { Log::info('Starting active taps synchronization'); // Data Access Layer - получение активных тапов $activeTaps = Tap::whereIn('status', [TapStatus::ACTIVE, TapStatus::NEW]) ->with('tapApp') ->get(); return $this->processTapsSynchronization($activeTaps, 'active_taps_sync'); } /** * Business Logic Layer - массовая синхронизация тапов конкретного пользователя */ public function syncUserTaps(int $userId): array { Log::info('Starting user taps synchronization', ['user_id' => $userId]); // Data Access Layer - получение тапов пользователя $userTaps = Tap::where('crm_user_id', $userId) ->with('tapApp') ->get(); return $this->processTapsSynchronization($userTaps, 'user_taps_sync', $userId); } /** * Business Logic Layer - синхронизация устаревших статусов */ public function syncStaleTaps(int $minutesThreshold = 5): array { Log::info('Starting stale taps synchronization', [ 'threshold_minutes' => $minutesThreshold ]); // Data Access Layer - получение тапов с устаревшими статусами $staleTaps = Tap::where(function ($query) use ($minutesThreshold) { $query->whereNull('last_process_check') ->orWhere('last_process_check', '<', now()->subMinutes($minutesThreshold)); }) ->with('tapApp') ->get(); return $this->processTapsSynchronization($staleTaps, 'stale_taps_sync'); } /** * Business Logic Layer - принудительная синхронизация всех тапов */ public function syncAllTaps(): array { Log::info('Starting full taps synchronization'); // Data Access Layer - получение всех тапов $allTaps = Tap::with('tapApp')->get(); return $this->processTapsSynchronization($allTaps, 'full_taps_sync'); } /** * Business Logic Layer - основной метод обработки синхронизации */ private function processTapsSynchronization(Collection $taps, string $syncType, ?int $userId = null): array { $startTime = microtime(true); $results = [ 'sync_type' => $syncType, 'user_id' => $userId, 'total_taps' => $taps->count(), 'processed_taps' => 0, 'synced_taps' => 0, 'changed_taps' => 0, 'error_taps' => 0, 'errors' => [], 'execution_time' => 0, 'success_rate' => 0, ]; if ($taps->isEmpty()) { Log::info('No taps found for synchronization', ['sync_type' => $syncType]); return $results; } // Обработка каждого тапа с batch processing $batchSize = 10; // Обрабатываем по 10 тапов одновременно $batches = $taps->chunk($batchSize); foreach ($batches as $batchIndex => $batch) { Log::debug('Processing taps batch', [ 'sync_type' => $syncType, 'batch_index' => $batchIndex + 1, 'batch_size' => $batch->count(), ]); foreach ($batch as $tap) { $results['processed_taps']++; try { $syncResult = $this->tapAppService->syncTapStatus($tap->id); if ($syncResult['synced']) { $results['synced_taps']++; if ($syncResult['status_changed'] ?? false) { $results['changed_taps']++; Log::info('Tap status synchronized and changed', [ 'tap_id' => $tap->id, 'sync_type' => $syncType, 'old_status' => $syncResult['old_status'] ?? 'unknown', 'new_status' => $syncResult['database_status'], 'supervisor_status' => $syncResult['supervisor_status'], ]); } } else { $results['error_taps']++; $error = "Tap {$tap->id}: " . ($syncResult['error'] ?? 'Unknown sync error'); $results['errors'][] = $error; } } catch (\Exception $e) { $results['error_taps']++; $error = "Tap {$tap->id}: Exception - {$e->getMessage()}"; $results['errors'][] = $error; Log::error('Exception during tap sync in batch processing', [ 'tap_id' => $tap->id, 'sync_type' => $syncType, 'batch_index' => $batchIndex + 1, 'error' => $e->getMessage(), ]); } } // Небольшая пауза между батчами для снижения нагрузки if ($batchIndex < $batches->count() - 1) { usleep(100000); // 100ms } } // Финализация результатов $results['execution_time'] = round(microtime(true) - $startTime, 2); $results['success_rate'] = $results['total_taps'] > 0 ? round(($results['synced_taps'] / $results['total_taps']) * 100, 1) : 0; Log::info('Taps synchronization completed', [ 'sync_type' => $syncType, 'results' => $results, ]); return $results; } /** * Business Logic Layer - получение статистики синхронизации */ public function getSyncStatistics(): array { // Data Access Layer - получение статистики из БД $totalTaps = Tap::count(); $activeTaps = Tap::where('status', TapStatus::ACTIVE)->count(); $staleTaps = Tap::where(function ($query) { $query->whereNull('last_process_check') ->orWhere('last_process_check', '<', now()->subMinutes(5)); })->count(); $recentlyChecked = Tap::where('last_process_check', '>=', now()->subMinutes(5))->count(); return [ 'total_taps' => $totalTaps, 'active_taps' => $activeTaps, 'stale_taps' => $staleTaps, 'recently_checked' => $recentlyChecked, 'stale_percentage' => $totalTaps > 0 ? round(($staleTaps / $totalTaps) * 100, 1) : 0, 'sync_coverage' => $totalTaps > 0 ? round(($recentlyChecked / $totalTaps) * 100, 1) : 0, 'last_check_time' => now()->toDateTimeString(), ]; } /** * Business Logic Layer - очистка устаревших данных синхронизации */ public function cleanupSyncData(int $daysToKeep = 7): array { Log::info('Starting sync data cleanup', ['days_to_keep' => $daysToKeep]); $cutoffDate = now()->subDays($daysToKeep); // Очистка устаревших process_info данных $updatedTaps = Tap::whereNotNull('process_info') ->where('updated_at', '<', $cutoffDate) ->get(); $cleanedCount = 0; foreach ($updatedTaps as $tap) { $processInfo = $tap->process_info ?? []; // Удаляем устаревшие ключи из process_info $keysToClean = ['last_sync_at', 'sync_source', 'old_statuses']; $cleaned = false; foreach ($keysToClean as $key) { if (isset($processInfo[$key])) { unset($processInfo[$key]); $cleaned = true; } } if ($cleaned) { $tap->update(['process_info' => $processInfo]); $cleanedCount++; } } Log::info('Sync data cleanup completed', [ 'cleaned_taps' => $cleanedCount, 'cutoff_date' => $cutoffDate->toDateTimeString(), ]); return [ 'cleaned_taps' => $cleanedCount, 'cutoff_date' => $cutoffDate->toDateTimeString(), ]; } }