format('Y-m-d'); // API 사용량 $apiStats = DB::connection('mysql') ->table('api_request_logs') ->where('tenant_id', $tenantId) ->whereDate('created_at', $dateStr) ->selectRaw(' COUNT(*) as request_count, SUM(CASE WHEN response_status >= 400 THEN 1 ELSE 0 END) as error_count, COALESCE(AVG(duration_ms), 0) as avg_response_ms ') ->first(); // 사용자 활동 (고유 사용자 수, 로그인 수) $activeUsers = DB::connection('mysql') ->table('api_request_logs') ->where('tenant_id', $tenantId) ->whereDate('created_at', $dateStr) ->whereNotNull('user_id') ->distinct('user_id') ->count('user_id'); // personal_access_tokens에 tenant_id 없으므로 user_tenants 조인으로 로그인 수 집계 $loginCount = DB::connection('mysql') ->table('personal_access_tokens as pat') ->join('user_tenants as ut', function ($join) use ($tenantId) { $join->on('pat.tokenable_id', '=', 'ut.user_id') ->where('pat.tokenable_type', '=', 'App\\Models\\Members\\User') ->where('ut.tenant_id', '=', $tenantId); }) ->whereDate('pat.created_at', $dateStr) ->count(); // 감사 로그 $auditStats = DB::connection('mysql') ->table('audit_logs') ->where('tenant_id', $tenantId) ->whereDate('created_at', $dateStr) ->selectRaw(" SUM(CASE WHEN action = 'created' THEN 1 ELSE 0 END) as create_count, SUM(CASE WHEN action = 'updated' THEN 1 ELSE 0 END) as update_count, SUM(CASE WHEN action = 'deleted' THEN 1 ELSE 0 END) as delete_count ") ->first(); // FCM 알림 $fcmStats = DB::connection('mysql') ->table('fcm_send_logs') ->where('tenant_id', $tenantId) ->whereDate('created_at', $dateStr) ->selectRaw(' COALESCE(SUM(success_count), 0) as sent_count, COALESCE(SUM(failure_count), 0) as failed_count ') ->first(); // 파일 업로드 $fileStats = DB::connection('mysql') ->table('files') ->where('tenant_id', $tenantId) ->whereDate('created_at', $dateStr) ->selectRaw(' COUNT(*) as upload_count, COALESCE(SUM(file_size), 0) / 1048576 as upload_size_mb ') ->first(); // 결재 $approvalSubmitted = DB::connection('mysql') ->table('approvals') ->where('tenant_id', $tenantId) ->whereDate('drafted_at', $dateStr) ->whereNull('deleted_at') ->count(); $approvalCompleted = DB::connection('mysql') ->table('approvals') ->where('tenant_id', $tenantId) ->whereDate('completed_at', $dateStr) ->whereNull('deleted_at') ->count(); $approvalAvgHours = DB::connection('mysql') ->table('approvals') ->where('tenant_id', $tenantId) ->whereDate('completed_at', $dateStr) ->whereNotNull('drafted_at') ->whereNotNull('completed_at') ->whereNull('deleted_at') ->selectRaw('AVG(TIMESTAMPDIFF(HOUR, drafted_at, completed_at)) as avg_hours') ->value('avg_hours'); StatSystemDaily::updateOrCreate( ['tenant_id' => $tenantId, 'stat_date' => $dateStr], [ 'api_request_count' => $apiStats->request_count ?? 0, 'api_error_count' => $apiStats->error_count ?? 0, 'api_avg_response_ms' => (int) ($apiStats->avg_response_ms ?? 0), 'active_user_count' => $activeUsers, 'login_count' => $loginCount, 'audit_create_count' => $auditStats->create_count ?? 0, 'audit_update_count' => $auditStats->update_count ?? 0, 'audit_delete_count' => $auditStats->delete_count ?? 0, 'fcm_sent_count' => $fcmStats->sent_count ?? 0, 'fcm_failed_count' => $fcmStats->failed_count ?? 0, 'file_upload_count' => $fileStats->upload_count ?? 0, 'file_upload_size_mb' => $fileStats->upload_size_mb ?? 0, 'approval_submitted_count' => $approvalSubmitted, 'approval_completed_count' => $approvalCompleted, 'approval_avg_hours' => (float) ($approvalAvgHours ?? 0), ] ); return 1; } public function aggregateMonthly(int $tenantId, int $year, int $month): int { // 시스템 도메인은 일간 테이블만 운영 return 0; } }