exec("SET NAMES 'utf8mb4'"); $categoryBase = [ 'CA10001' => '마이클래스', 'CA10002' => '온보딩', 'CA10003' => '법정교육', 'CA10004' => '리더십', 'CA10005' => '인사이트', 'CA10006' => '비즈트렌드', ]; $stmtCode = $pdo->prepare(" SELECT base_code, code_name FROM edu_codes WHERE group_code = 'CA100' AND is_active = '1' ORDER BY code "); $stmtCode->execute(); foreach ($stmtCode->fetchAll(PDO::FETCH_ASSOC) as $row) { $categoryBase[$row['base_code']] = $row['code_name']; } // 연도 목록 $stmtYear = $pdo->prepare(" SELECT DISTINCT YEAR(last_viewed_at) AS view_year FROM edu_learning_histories WHERE member_id = :member_id AND sys_comp_code = :sys_comp_code AND last_viewed_at IS NOT NULL ORDER BY view_year DESC "); $stmtYear->execute([ ':member_id' => $memberId, ':sys_comp_code' => $sysCompCode, ]); $yearList = array_map( static fn($v) => (int)$v, array_filter($stmtYear->fetchAll(PDO::FETCH_COLUMN)) ); $selectedYear = isset($_GET['year']) ? (int)$_GET['year'] : 0; if ($selectedYear <= 0) { $selectedYear = !empty($yearList) ? (int)$yearList[0] : (int)date('Y'); } // 내 총학습시간 $stmtMyTotal = $pdo->prepare(" SELECT COALESCE(SUM(watch_tm), 0) AS total_watch_tm FROM edu_learning_histories WHERE member_id = :member_id AND sys_comp_code = :sys_comp_code AND YEAR(last_viewed_at) = :stats_year "); $stmtMyTotal->execute([ ':member_id' => $memberId, ':sys_comp_code' => $sysCompCode, ':stats_year' => $selectedYear, ]); $myTotalMinutes = (int)$stmtMyTotal->fetchColumn(); // 전체평균 $stmtAvg = $pdo->prepare(" SELECT COALESCE(AVG(x.user_total_watch_tm), 0) AS avg_total_watch_tm FROM ( SELECT member_id, sys_comp_code, SUM(watch_tm) AS user_total_watch_tm FROM edu_learning_histories WHERE YEAR(last_viewed_at) = :stats_year GROUP BY member_id, sys_comp_code ) x "); $stmtAvg->execute([':stats_year' => $selectedYear]); $avgTotalMinutes = (int)round((float)$stmtAvg->fetchColumn()); // 온보딩 기준일 $stmtUser = $pdo->prepare(" SELECT join_date FROM edu_users WHERE member_id = :member_id AND sys_comp_code = :sys_comp_code LIMIT 1 "); $stmtUser->execute([ ':member_id' => $memberId, ':sys_comp_code' => $sysCompCode, ]); $joinDate = $stmtUser->fetchColumn(); $onboardingDueDate = ''; $isOnboardingPeriod = 'N'; if (!empty($joinDate)) { $onboardingDueDate = date('Y-m-d', strtotime($joinDate . ' +14 days')); $isOnboardingPeriod = (date('Y-m-d') <= $onboardingDueDate) ? 'Y' : 'N'; } // 선택연도 카테고리별 집계 $stmtCategory = $pdo->prepare(" SELECT c.category_code, COUNT(DISTINCT lh.content_id) AS learned_content_cnt, COUNT(DISTINCT CASE WHEN lh.watch_tm >= lh.content_tm THEN lh.content_id END) AS completed_content_cnt, COALESCE(SUM(lh.watch_tm), 0) AS total_watch_tm, COALESCE(SUM(lh.content_tm), 0) AS total_content_tm FROM edu_learning_histories lh INNER JOIN edu_contents c ON c.content_id = lh.content_id WHERE lh.member_id = :member_id AND lh.sys_comp_code = :sys_comp_code AND YEAR(lh.last_viewed_at) = :stats_year GROUP BY c.category_code "); $stmtCategory->execute([ ':member_id' => $memberId, ':sys_comp_code' => $sysCompCode, ':stats_year' => $selectedYear, ]); $aggMap = []; foreach ($stmtCategory->fetchAll(PDO::FETCH_ASSOC) as $row) { $aggMap[$row['category_code']] = $row; } // 카테고리별 전체 콘텐츠 수 $stmtTotalCnt = $pdo->prepare(" SELECT category_code, COUNT(*) AS total_cnt FROM edu_contents WHERE (is_active = '1' OR is_active = 'Y') GROUP BY category_code "); $stmtTotalCnt->execute(); $totalCntMap = []; foreach ($stmtTotalCnt->fetchAll(PDO::FETCH_ASSOC) as $row) { $totalCntMap[$row['category_code']] = (int)$row['total_cnt']; } $activityItems = []; foreach ($categoryBase as $categoryCode => $categoryName) { $agg = $aggMap[$categoryCode] ?? null; $learnedContentCnt = (int)($agg['learned_content_cnt'] ?? 0); $completedContentCnt = (int)($agg['completed_content_cnt'] ?? 0); $totalWatchTm = (int)($agg['total_watch_tm'] ?? 0); $totalContentTm = (int)($agg['total_content_tm'] ?? 0); $masterTotalCnt = (int)($totalCntMap[$categoryCode] ?? 0); // 1차안: gauge는 학습이력 기준 content_tm 합계가 있을 때 그 기준으로 계산 // schema상 edu_contents에 표준 duration 컬럼이 없어 완전한 "전체 기준 분모"는 별도 운영정책 확정 필요 $percent = ($totalContentTm > 0) ? (int)round(($totalWatchTm / $totalContentTm) * 100) : 0; $percent = max(0, min(100, $percent)); $activityItems[] = [ 'category_code' => $categoryCode, 'category_name' => $categoryName, 'learned_content_cnt' => $learnedContentCnt, 'completed_content_cnt' => $completedContentCnt, 'master_total_cnt' => $masterTotalCnt, 'total_watch_tm' => $totalWatchTm, 'total_content_tm' => $totalContentTm, 'percent' => $percent, 'onboarding_due_date' => ($categoryCode === 'CA10002') ? $onboardingDueDate : '', 'is_onboarding_period' => ($categoryCode === 'CA10002') ? $isOnboardingPeriod : 'N', ]; } response_json([ 'success' => true, 'year_list' => $yearList, 'selected_year' => $selectedYear, 'my_total_minutes' => $myTotalMinutes, 'avg_total_minutes' => $avgTotalMinutes, 'onboarding_due_date'=> $onboardingDueDate, 'is_onboarding_period' => $isOnboardingPeriod, 'activity_items' => $activityItems, ]); } catch (Throwable $e) { response_json([ 'success' => false, 'message' => '학습활동 데이터를 불러오지 못했습니다.', 'error' => $e->getMessage(), ]); }