prepare( 'SELECT name, rank_name, sys_comp_code FROM edu_users WHERE member_id = ? AND sys_comp_code = ? LIMIT 1' ); $__st->execute([$memberId, $sysCompCode]); } else { $__st = db_conn()->prepare( 'SELECT name, rank_name, sys_comp_code FROM edu_users WHERE member_id = ? ORDER BY sys_comp_code LIMIT 1' ); $__st->execute([$memberId]); } $__row = $__st->fetch(PDO::FETCH_ASSOC); if ($__row) { if ($userName === '') $userName = (string) ($__row['name'] ?? ''); if ($userRank === '') $userRank = (string) ($__row['rank_name'] ?? ''); if ($sysCompCode === '') $sysCompCode = (string) ($__row['sys_comp_code'] ?? ''); } } catch (Throwable $__e) { error_log('[main_data rank fallback] ' . $__e->getMessage()); } } $userRank = trim($userRank); // ── 키워드 & 영상 설정 ───────────────────────────────────────── $allKeywords = []; // 전체 키워드 목록 (모달 선택용, 21개) $adminKeywords = []; // 회사 추천 키워드 (kw-deny) $myKeywords = []; // 사용자 선택 키워드 0~3개 (kw-allow) $videosJson = '[]'; $myKwJson = '[]'; // JS fallback용 $totalMin = 0; $avgWatchMin = 0; // 전체 평균 학습시간 (분) $mainDataError = ''; // DEBUG용 오류 메시지 try { $pdo = db_conn(); $pdo->exec("SET NAMES 'utf8mb4'"); // edu_user_keywords 테이블 자동 생성 (권한 없으면 무시) try { $pdo->exec(" CREATE TABLE IF NOT EXISTS edu_user_keywords ( member_id VARCHAR(20) NOT NULL, sys_comp_code VARCHAR(20) NOT NULL DEFAULT '', keyword_code VARCHAR(20) NOT NULL, sort_order INT NOT NULL DEFAULT 0, PRIMARY KEY (member_id, sys_comp_code, keyword_code) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 "); } catch (Throwable $__ce) { error_log('[main_data] CREATE edu_user_keywords: ' . $__ce->getMessage()); } // 전체 키워드 (모달 선택용) — sort_order 없는 테이블도 안전하게 base_code만으로 정렬 $stmtKw = $pdo->query("SELECT base_code AS keyword_code, code_name AS keyword_name FROM edu_codes WHERE group_code = 'KW100' ORDER BY base_code"); $allKeywords = $stmtKw->fetchAll(PDO::FETCH_ASSOC); // 회사 추천 키워드: 반드시 edu_recommend_keywords의 코드값을 edu_codes와 한글 매핑해서 사용 try { $stmtAdm = $pdo->prepare(" SELECT rk.keyword_code, ec.code_name AS keyword_name FROM edu_recommend_keywords rk JOIN edu_codes ec ON ec.base_code = rk.keyword_code WHERE rk.is_active = 1 AND rk.keyword_code IS NOT NULL AND rk.keyword_code <> '' AND ec.group_code = 'KW100' ORDER BY rk.keyword_code LIMIT 2 "); $stmtAdm->execute(); $adminKeywords = $stmtAdm->fetchAll(PDO::FETCH_ASSOC); error_log('[main_data] edu_recommend_keywords result: ' . count($adminKeywords) . ' rows'); } catch (Throwable $__ae) { error_log('[main_data] edu_recommend_keywords 쿼리 실패: ' . $__ae->getMessage()); $adminKeywords = []; } // 사용자 선택 키워드 (0~3개) // ⚠️ sys_comp_code 없어도 저장되도록 조건 완화 (저장 후 바로 조회 위함) // sys_comp_code 조건 추가 : 권오재 if ($memberId !== '') { $stmtMy = $pdo->prepare(" SELECT uk.keyword_code, ec.code_name AS keyword_name FROM edu_user_keywords uk JOIN edu_codes ec ON ec.base_code = uk.keyword_code WHERE uk.member_id = ? AND uk.sys_comp_code = ? AND uk.sys_comp_code <> '' ORDER BY uk.keyword_code LIMIT 3 "); $stmtMy->execute([$memberId, $sysCompCode]); $myKeywords = $stmtMy->fetchAll(PDO::FETCH_ASSOC); error_log('[main_data] myKeywords query: memberId=' . $memberId . ', result_count=' . count($myKeywords)); } // JS fallback용: 사용자 키워드명 배열 (JSON) $myKwJson = json_encode( array_column($myKeywords, 'keyword_name'), JSON_UNESCAPED_UNICODE | JSON_HEX_TAG ); // 누적 시청 시간 (게이지): watch_tm 기준 if ($memberId !== '' && $sysCompCode !== '') { $stmtTm = $pdo->prepare( 'SELECT COALESCE(SUM(watch_tm), 0) FROM edu_learning_histories WHERE member_id = ? AND sys_comp_code = ?' ); $stmtTm->execute([$memberId, $sysCompCode]); $totalMin = (int) floor((int) $stmtTm->fetchColumn() / 60); } // 전체 평균 학습시간 계산: (모든 member_id의 총 watch_tm 합계) / (학습경험이 있는 사용자 수) try { $stmtAvg = $pdo->prepare(" SELECT COALESCE(SUM(h.watch_tm), 0) as total_watch_tm, COUNT(DISTINCT h.member_id) as unique_members FROM edu_learning_histories h WHERE h.sys_comp_code = ? "); $stmtAvg->execute([$sysCompCode]); $avgRow = $stmtAvg->fetch(PDO::FETCH_ASSOC); if ($avgRow) { $totalWatchTm = (int) ($avgRow['total_watch_tm'] ?? 0); $uniqueMembers = (int) ($avgRow['unique_members'] ?? 0); if ($uniqueMembers > 0) { $avgWatchMin = (int) floor($totalWatchTm / $uniqueMembers / 60); // 최소 50분으로 설정해서 게이지의 maxValue 보장 $avgWatchMin = max(50, $avgWatchMin); error_log('[main_data] avgWatchMin calc: totalWatchTm=' . $totalWatchTm . ', uniqueMembers=' . $uniqueMembers . ', avgWatchMin=' . $avgWatchMin); } } } catch (Throwable $__ae) { error_log('[main_data] avgWatchMin 계산 실패: ' . $__ae->getMessage()); } // ── Pick 영상(추천) + 키워드 영상 5개 조합: 항상 6개 보장 ── $videos = []; // 1. Pick(추천) 영상: is_offer=1, 본인 제외, 랜덤 1개 $stmtPick = $pdo->prepare("SELECT * FROM edu_contents WHERE is_offer=1 AND is_active=1 AND issue_type_code = 'IS10003' AND member_id != ? ORDER BY RAND() LIMIT 1"); $stmtPick->execute([$memberId]); $pick = $stmtPick->fetch(PDO::FETCH_ASSOC); if ($pick) { $pick['is_pick'] = true; $videos[] = $pick; } // 2. 나머지 5개: 실제 사용자 키워드 기반 영상 쿼리로 채움 $keywordVideos = []; $userKeywordCodes = array_column($myKeywords, 'keyword_code'); if (!empty($userKeywordCodes)) { $ph = implode(',', array_fill(0, count($userKeywordCodes), '?')); $sql = "SELECT * FROM edu_contents c JOIN edu_content_keywords k ON c.content_id = k.content_id AND k.is_active = 1 WHERE c.is_active=1 AND k.keyword_code IN ($ph)"; $params = $userKeywordCodes; // pick과 중복 제거 if ($pick && isset($pick['content_id'])) { $sql .= " AND c.content_id != ?"; $params[] = $pick['content_id']; } $sql .= " GROUP BY c.content_id ORDER BY c.content_id DESC LIMIT 5"; $stmt = $pdo->prepare($sql); $stmt->execute($params); foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) { $row['is_pick'] = false; $keywordVideos[] = $row; } } // 부족하면 전체 영상에서 추가로 채움 if (count($keywordVideos) < 5) { $already = array_column($keywordVideos, 'content_id'); if ($pick && isset($pick['content_id'])) $already[] = $pick['content_id']; $ph = implode(',', array_fill(0, count($already), '?')); $sql = "SELECT * FROM edu_contents WHERE is_active=1"; if (!empty($already)) $sql .= " AND content_id NOT IN ($ph)"; $sql .= " ORDER BY content_id DESC LIMIT " . (5 - count($keywordVideos)); $stmt = $pdo->prepare($sql); $stmt->execute($already); foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) { $row['is_pick'] = false; $keywordVideos[] = $row; } } // 최대 5개만 $keywordVideos = array_slice($keywordVideos, 0, 5); foreach ($keywordVideos as $v) { $videos[] = $v; } // 3. 부족하면 빈 카드로 채움 while (count($videos) < 6) { $videos[] = [ 'content_id' => 'empty_' . count($videos), 'title' => '', 'is_pick' => false, 'img' => '', 'category' => '', 'author' => '', 'keywords' => [], ]; } // 카테고리 코드→한글명 매핑 (edu_codes) $codeMap = []; $normCode = static function (string $code): string { return preg_replace('/[^A-Z0-9]/', '', strtoupper(trim($code))); }; try { $stmtCm = $pdo->query("SELECT base_code, code_name FROM edu_codes"); foreach ($stmtCm->fetchAll(PDO::FETCH_ASSOC) as $cr) { $baseCode = $normCode((string) ($cr['base_code'] ?? '')); if ($baseCode === '') continue; $codeMap[$baseCode] = $cr['code_name']; } } catch (Throwable $e) { /* ignore */ } foreach ($videos as &$v) { if (!is_array($v) || !isset($v['content_id']) || strpos((string) ($v['content_id'] ?? ''), 'empty_') === 0) continue; $categoryCode = $normCode((string) ($v['category_code'] ?? '')); $groupCode = $normCode((string) ($v['category_group'] ?? '')); $v['category'] = $codeMap[$categoryCode] ?? ($v['category_code'] ?? ''); $v['subcate'] = $codeMap[$groupCode] ?? ($v['category_group'] ?? ''); } unset($v); // 4. JSON 직렬화 $videosJson = json_encode($videos, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG); } catch (Throwable $e) { $mainDataError = $e->getMessage(); error_log('[main_data] ' . $e->getMessage()); }