prepare('SELECT sys_comp_code FROM edu_users WHERE member_id = ? ORDER BY sys_comp_code LIMIT 1'); $stmtUser->execute([$memberId]); $sysCompCode = (string)($stmtUser->fetchColumn() ?: ''); if ($sysCompCode === '') { json_exit([ 'success' => false, 'message' => 'edu_users에 회원 정보가 없어 마이클래스 상태를 조회할 수 없습니다.', 'data' => ['member_id' => $memberId], ], 400); } return [$memberId, $sysCompCode]; } function ensure_goal_contents(PDO $pdo, string $goalCode, string $quarter, string $actor): int { $stmtCnt = $pdo->prepare("SELECT COUNT(*) FROM edu_goal_contents WHERE goal_code = ? AND (is_active = '1' OR is_active IS NULL)"); $stmtCnt->execute([$goalCode]); $existing = (int)$stmtCnt->fetchColumn(); if ($existing >= 6) { return $existing; } $stmtCandidates = $pdo->prepare( "SELECT c.content_id FROM edu_contents c WHERE (c.is_active = '1' OR c.is_active IS NULL) AND (c.goal_code = :goal_code OR c.category_group = :quarter) ORDER BY CASE WHEN c.sort_order IS NULL THEN 1 ELSE 0 END, c.sort_order ASC, c.content_id ASC LIMIT 6" ); $stmtCandidates->execute([ ':goal_code' => $goalCode, ':quarter' => $quarter, ]); $candidateIds = $stmtCandidates->fetchAll(PDO::FETCH_COLUMN); if (count($candidateIds) < 6) { $stmtFallback = $pdo->prepare( "SELECT c.content_id FROM edu_contents c WHERE (c.is_active = '1' OR c.is_active IS NULL) ORDER BY CASE WHEN c.sort_order IS NULL THEN 1 ELSE 0 END, c.sort_order ASC, c.content_id ASC LIMIT 6" ); $stmtFallback->execute(); $candidateIds = $stmtFallback->fetchAll(PDO::FETCH_COLUMN); } $candidateIds = array_values(array_unique(array_filter(array_map('strval', $candidateIds)))); $stmtUpsert = $pdo->prepare( "INSERT INTO edu_goal_contents (goal_code, content_id, index_id, is_active, sort_order, created_by, created_at, updated_by, updated_at) VALUES (?, ?, ?, '1', ?, ?, NOW(), ?, NOW()) ON DUPLICATE KEY UPDATE is_active = VALUES(is_active), sort_order = VALUES(sort_order), updated_by = VALUES(updated_by), updated_at = NOW()" ); foreach ($candidateIds as $idx => $contentId) { $order = $idx + 1; $stmtUpsert->execute([$goalCode, $contentId, $order, $order, $actor, $actor]); } $stmtCnt->execute([$goalCode]); return (int)$stmtCnt->fetchColumn(); } try { $pdo = db_conn(); [$memberId, $sysCompCode] = resolve_member_and_company($pdo); $stmtGoals = $pdo->query( "SELECT goal_code, title, quarter, goal_no, sort_order FROM edu_learning_goals WHERE (is_active = '1' OR is_active IS NULL) ORDER BY base_year DESC, quarter ASC, goal_no ASC, goal_code ASC" ); $goals = $stmtGoals->fetchAll(PDO::FETCH_ASSOC); if (!$goals) { json_exit(['success' => true, 'data' => ['goals' => [], 'videos' => []]]); } $stmtSelected = $pdo->prepare( "SELECT ug.goal_code, ug.quarter, ug.completed_date, ug.updated_at, lg.title FROM edu_user_learning_goals ug LEFT JOIN edu_learning_goals lg ON lg.goal_code = ug.goal_code WHERE ug.member_id = ? AND ug.sys_comp_code = ? AND (ug.is_active = '1' OR ug.is_active IS NULL) ORDER BY ug.updated_at DESC, ug.goal_code DESC" ); $stmtSelected->execute([$memberId, $sysCompCode]); $selectedGoals = $stmtSelected->fetchAll(PDO::FETCH_ASSOC); $requestedGoalCode = trim((string)($_GET['goal_code'] ?? '')); $activeGoalCode = $requestedGoalCode; if ($activeGoalCode === '' && !empty($selectedGoals)) { $activeGoalCode = (string)$selectedGoals[0]['goal_code']; } if ($activeGoalCode === '') { $activeGoalCode = (string)$goals[0]['goal_code']; } $goalIndex = []; foreach ($goals as $g) { $goalIndex[(string)$g['goal_code']] = $g; } if (!isset($goalIndex[$activeGoalCode])) { $activeGoalCode = (string)$goals[0]['goal_code']; } $activeQuarter = (string)($goalIndex[$activeGoalCode]['quarter'] ?? ''); ensure_goal_contents($pdo, $activeGoalCode, $activeQuarter, $memberId); $stmtVideos = $pdo->prepare( "SELECT gc.goal_code, gc.content_id, gc.sort_order, gc.index_id, c.title, c.description, c.description2, c.content_url, c.thumbnail_url, c.category_code, c.category_group, lh.watch_tm, lh.content_tm, lh.completed_at FROM edu_goal_contents gc JOIN edu_contents c ON c.content_id = gc.content_id LEFT JOIN edu_learning_histories lh ON lh.member_id = ? AND lh.sys_comp_code = ? AND lh.content_id = gc.content_id WHERE gc.goal_code = ? AND (gc.is_active = '1' OR gc.is_active IS NULL) ORDER BY CASE WHEN gc.sort_order IS NULL THEN 1 ELSE 0 END, gc.sort_order ASC, CASE WHEN gc.index_id IS NULL THEN 1 ELSE 0 END, gc.index_id ASC, gc.content_id ASC LIMIT 6" ); $stmtVideos->execute([$memberId, $sysCompCode, $activeGoalCode]); $videos = $stmtVideos->fetchAll(PDO::FETCH_ASSOC); $contentIds = array_values(array_unique(array_filter(array_map(static function ($r) { return (string)($r['content_id'] ?? ''); }, $videos)))); $memoMap = []; if (!empty($contentIds)) { $in = implode(',', array_fill(0, count($contentIds), '?')); $stmtMemo = $pdo->prepare( "SELECT content_id, seq, title FROM edu_content_memos WHERE content_id IN ($in) AND (is_active = '1' OR is_active IS NULL) ORDER BY content_id ASC, seq ASC" ); $stmtMemo->execute($contentIds); $memoRows = $stmtMemo->fetchAll(PDO::FETCH_ASSOC); foreach ($memoRows as $m) { $cid = (string)$m['content_id']; if (!isset($memoMap[$cid])) { $memoMap[$cid] = []; } $memoTitle = trim((string)($m['title'] ?? '')); if ($memoTitle !== '') { $memoMap[$cid][] = $memoTitle; } } } foreach ($videos as $idx => $video) { $cid = (string)$video['content_id']; $videos[$idx]['memos'] = $memoMap[$cid] ?? []; $videos[$idx]['is_completed'] = !empty($video['completed_at']); $videos[$idx]['slot_no'] = $idx + 1; } $stmtProgress = $pdo->prepare( "SELECT ug.goal_code, COUNT(gc.content_id) AS total_count, SUM(CASE WHEN lh.completed_at IS NOT NULL THEN 1 ELSE 0 END) AS completed_count FROM edu_user_learning_goals ug LEFT JOIN edu_goal_contents gc ON gc.goal_code = ug.goal_code AND (gc.is_active = '1' OR gc.is_active IS NULL) LEFT JOIN edu_learning_histories lh ON lh.member_id = ug.member_id AND lh.sys_comp_code = ug.sys_comp_code AND lh.content_id = gc.content_id WHERE ug.member_id = ? AND ug.sys_comp_code = ? AND (ug.is_active = '1' OR ug.is_active IS NULL) GROUP BY ug.goal_code" ); $stmtProgress->execute([$memberId, $sysCompCode]); $progressRows = $stmtProgress->fetchAll(PDO::FETCH_ASSOC); $goalProgress = []; foreach ($progressRows as $row) { $total = (int)($row['total_count'] ?? 0); $completed = (int)($row['completed_count'] ?? 0); $goalProgress[(string)$row['goal_code']] = [ 'total_count' => $total, 'completed_count' => $completed, 'is_completed' => $total > 0 && $completed >= $total, ]; } json_exit([ 'success' => true, 'data' => [ 'member_id' => $memberId, 'sys_comp_code' => $sysCompCode, 'active_goal_code' => $activeGoalCode, 'goals' => $goals, 'selected_goals' => $selectedGoals, 'goal_progress' => $goalProgress, 'videos' => $videos, ], ]); } catch (Throwable $e) { json_exit([ 'success' => false, 'message' => $e->getMessage(), ], 500); }