$limit, "page" => $page]); $ch = curl_init($url_users); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($http_code !== 200) { echo json_encode(['status' => 'fail', 'step' => 'user_search', 'code' => $http_code, 'raw' => $response]); exit; } $res = json_decode($response, true); $users = $res['users'] ?? []; $all_users = array_merge($all_users, $users); $page++; } while (count($users) === $limit); // ✅ 2) MySQL에서 최근 로그인 시간 병합 try { $dbHost = getenv('G5_MYSQL_HOST') ?: getenv('MARIADB_HOST') ?: getenv('MYSQL_HOST') ?: 'localhost'; $dbName = getenv('G5_MYSQL_DB') ?: getenv('MARIADB_DATABASE') ?: getenv('MYSQL_DATABASE') ?: 'egbim'; $dbUser = getenv('G5_MYSQL_USER') ?: getenv('MARIADB_USER') ?: getenv('MYSQL_USER') ?: 'egbim'; $dbPass = getenv('G5_MYSQL_PASSWORD') ?: getenv('MARIADB_PASSWORD') ?: getenv('MYSQL_PASSWORD') ?: 'baron3840!!'; $pdo = new PDO("mysql:host={$dbHost};dbname={$dbName};charset=utf8mb4", $dbUser, $dbPass, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]); $stmt = $pdo->query(" SELECT login_id, MAX(user_login_time) AS last_login_time, SUM( CASE WHEN product_name IN ('eg-bim', 'EG-BIM') AND user_login_time >= DATE_SUB(NOW(), INTERVAL 1 MONTH) THEN 1 ELSE 0 END ) AS recent_login_count_1m, COUNT( DISTINCT CASE WHEN product_name IN ('eg-bim', 'EG-BIM') AND created_at >= DATE_SUB(NOW(), INTERVAL 1 MONTH) THEN DATE(created_at) ELSE NULL END ) AS recent_login_days_1m FROM user_program_info WHERE ( (user_login_time IS NOT NULL AND user_login_time <> '') OR created_at IS NOT NULL ) GROUP BY login_id "); $loginMap = []; $recentLoginCount1mMap = []; $recentLoginDays1mMap = []; while ($row = $stmt->fetch()) { $loginMap[$row['login_id']] = $row['last_login_time']; $recentLoginCount1mMap[$row['login_id']] = (int) ($row['recent_login_count_1m'] ?? 0); $recentLoginDays1mMap[$row['login_id']] = (int) ($row['recent_login_days_1m'] ?? 0); } } catch (Exception $e) { $loginMap = []; $recentLoginCount1mMap = []; $recentLoginDays1mMap = []; } // ✅ 3) 날짜 변환 함수 (KST 환경 기준 — 이중 변환 방지) $fmtDate = function($val) { if (empty($val)) return null; if (!is_numeric($val)) return null; $ts = (float) $val; if ($ts > 1e12) $ts = $ts / 1000; // 밀리초 → 초 변환 $ts = (int) round($ts); if ($ts <= 0) return null; // ✅ UTC 기준으로 그대로 출력 (PHP의 +9h 보정 방지) return gmdate('Y-m-d H:i:s', $ts); }; // ✅ 4) 유저 데이터 가공 foreach ($all_users as &$u) { $u['customAttributes'] = $u['customAttributes'] ?? []; $u['name'] = $u['name'] ?? ''; $u['email'] = $u['email'] ?? ''; $u['phone'] = $u['phone'] ?? ''; // --- 테넌트 이름 문자열화 --- if (!empty($u['userTenants']) && is_array($u['userTenants'])) { $tenantNames = []; foreach ($u['userTenants'] as $t) { $tenantNames[] = $t['tenantName'] ?? $t['tenantId'] ?? ''; } $u['tenantText'] = implode(', ', array_filter($tenantNames)); } else { $u['tenantText'] = '-'; } // --- createdTime --- if (!empty($u['createdTime'])) { $u['createdTime'] = $fmtDate($u['createdTime']); } // --- egBimLExpiryDate --- if (!empty($u['customAttributes']['egBimLExpiryDate'])) { $u['customAttributes']['egBimLExpiryDate'] = $fmtDate($u['customAttributes']['egBimLExpiryDate']); } // --- lastSignInDate --- if (!empty($u['customAttributes']['lastSignInDate'])) { $u['customAttributes']['lastSignInDate'] = $fmtDate($u['customAttributes']['lastSignInDate']); } // --- serverLastLogin (EGBIM DB 병합) --- $loginId = $u['loginIds'][0] ?? null; if ($loginId && isset($loginMap[$loginId])) { $u['customAttributes']['serverLastLogin'] = $loginMap[$loginId]; } $u['customAttributes']['recentLoginCount1m'] = $loginId && isset($recentLoginCount1mMap[$loginId]) ? $recentLoginCount1mMap[$loginId] : 0; $u['customAttributes']['recentLoginDays1m'] = $loginId && isset($recentLoginDays1mMap[$loginId]) ? $recentLoginDays1mMap[$loginId] : 0; // --- completeForm 표시를 true/false → 텍스트로 --- if (isset($u['customAttributes']['completeForm'])) { $u['customAttributes']['completeForm'] = $u['customAttributes']['completeForm'] ? '완료' : '미완료'; } } unset($u); // ✅ 5) 필터링 $start = $_GET['start'] ?? null; $end = $_GET['end'] ?? null; $mode = $_GET['mode'] ?? 'created'; $keyword = $_GET['keyword'] ?? ''; $tenantFilters = isset($_GET['tenants']) ? explode(',', $_GET['tenants']) : []; function normalize_ts($val) { $ts = (int)$val; if ($ts > 0 && $ts < 1e12) $ts *= 1000; return $ts; } // --- 기간 필터 --- if ($start || $end) { $startTs = $start ? strtotime($start . " 00:00:00") * 1000 : null; $endTs = $end ? strtotime($end . " 23:59:59") * 1000 : null; $all_users = array_filter($all_users, function($u) use ($startTs, $endTs, $mode) { $ts = 0; if ($mode === 'created') { $ts = strtotime($u['createdTime'] ?? '') * 1000; } elseif ($mode === 'lastSignIn') { $ts = strtotime($u['customAttributes']['lastSignInDate'] ?? '') * 1000; } elseif ($mode === 'license') { $ts = strtotime($u['customAttributes']['egBimLExpiryDate'] ?? '') * 1000; } elseif ($mode === 'serverLogin') { if (!empty($u['customAttributes']['serverLastLogin'])) { $ts = strtotime($u['customAttributes']['serverLastLogin']) * 1000; } } if (!$ts) return false; if ($startTs && $ts < $startTs) return false; if ($endTs && $ts > $endTs) return false; return true; }); $all_users = array_values($all_users); } // --- 테넌트 필터 --- if (!empty($tenantFilters)) { $all_users = array_filter($all_users, function($u) use ($tenantFilters) { $txt = $u['tenantText'] ?? ''; foreach ($tenantFilters as $t) { if (stripos($txt, $t) !== false) return true; } return false; }); $all_users = array_values($all_users); } // --- 키워드 필터 --- if ($keyword !== '') { $all_users = array_filter($all_users, function($u) use ($keyword) { $fields = [ $u['customAttributes']['familyCompany'] ?? '', $u['customAttributes']['company'] ?? '', $u['name'] ?? '', $u['displayName'] ?? '' ]; foreach ($fields as $f) { if (stripos($f, $keyword) !== false) return true; } return false; }); $all_users = array_values($all_users); } // --- 정렬 --- usort($all_users, function($a, $b) use ($mode) { if ($mode === 'created') { return strcmp($b['createdTime'] ?? '', $a['createdTime'] ?? ''); } elseif ($mode === 'lastSignIn') { return strcmp($b['customAttributes']['lastSignInDate'] ?? '', $a['customAttributes']['lastSignInDate'] ?? ''); } elseif ($mode === 'license') { return strcmp($b['customAttributes']['egBimLExpiryDate'] ?? '', $a['customAttributes']['egBimLExpiryDate'] ?? ''); } elseif ($mode === 'serverLogin') { return strcmp($b['customAttributes']['serverLastLogin'] ?? '', $a['customAttributes']['serverLastLogin'] ?? ''); } return 0; }); // ✅ 6) 최종 응답 (타임존 정보 포함) echo json_encode([ 'status' => 'success', // ✅ "ok" 대신 "success"로 'total' => count($all_users), 'records' => $all_users, 'msg' => '', // ✅ w2ui 기본 필드 추가 'meta' => [ 'timezone' => $server_timezone, 'server_time' => $server_time ] ], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); ?>