false, 'message' => 'not_logged_in'], JSON_UNESCAPED_UNICODE); exit; } try { $pdo = db_conn(); $fixedLegalNameMap = [ 'C01' => '개인정보보호', 'C02' => '직장내 괴롭힘 예방', 'C03' => '장애인 인식 개선', 'C04' => '성희롱 예방 교육', 'C05' => '퇴직금 교육', ]; $normalizeLegalGroupCode = static function ($value) { $code = strtoupper(trim((string)$value)); if (preg_match('/^CA200(C\d{2})$/', $code, $m)) { return $m[1]; } if (preg_match('/^C\d{2}$/', $code)) { return $code; } return ''; }; $legalCodes = array_keys($fixedLegalNameMap); $legalRawCodes = []; foreach ($legalCodes as $code) { $legalRawCodes[] = $code; $legalRawCodes[] = 'CA200' . $code; } $legalRawCodes = array_values(array_unique($legalRawCodes)); $phCodes = implode(',', array_fill(0, count($legalRawCodes), '?')); // 법정교육(CA10003) 컨텐츠를 category_group 기준으로 조회 $sql = " SELECT c.content_id, c.title, c.content_url, c.thumbnail_url, c.category_code, c.category_group, c.description, c.sort_order, lh.watch_tm, lh.content_tm, lh.all_tm, lh.completed_at FROM edu_contents c LEFT JOIN edu_learning_histories lh ON lh.content_id = c.content_id AND lh.member_id = ? AND lh.sys_comp_code = ? WHERE c.category_code = 'CA10003' AND c.is_active = 1 AND c.category_group IN ($phCodes) ORDER BY c.category_group ASC, c.sort_order ASC, c.content_id ASC "; $stmt = $pdo->prepare($sql); $stmt->execute(array_merge([$memberId, $sysCompCode], $legalRawCodes)); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: []; $chapterMap = []; foreach ($legalCodes as $index => $code) { $chapterMap[$code] = [ 'code' => $code, 'name' => $fixedLegalNameMap[$code] ?? $code, 'lessons' => [], ]; } foreach ($rows as $row) { $groupCode = $normalizeLegalGroupCode($row['category_group'] ?? ''); if ($groupCode === '' || !isset($chapterMap[$groupCode])) { continue; } // YouTube videoId 추출 $raw = trim($row['content_url'] ?? ''); $videoId = ''; if (preg_match('/(?:v=|youtu\.be\/)([A-Za-z0-9_-]{11})/', $raw, $m)) { $videoId = $m[1]; } elseif (preg_match('/^[A-Za-z0-9_-]{11}$/', $raw)) { $videoId = $raw; } $title = $row['title'] ?? ''; $watchTm = (int)($row['watch_tm'] ?? 0); $contentTm = (int)($row['content_tm'] ?? 0); $completed = !empty($row['completed_at']); $chapterMap[$groupCode]['lessons'][] = [ 'content_id' => $row['content_id'] ?? '', 'title' => $title, 'url' => $videoId, 'description'=> (string)($row['description'] ?? ''), 'sort_order' => (int)($row['sort_order'] ?? 0), 'watch_tm' => $watchTm, 'content_tm' => $contentTm, 'all_tm' => (int)($row['all_tm'] ?? 0), 'completed' => $completed, ]; } $chapters = []; foreach ($legalCodes as $code) { $chapter = $chapterMap[$code]; usort($chapter['lessons'], static function ($left, $right) { $sortDiff = (int)($left['sort_order'] ?? 0) <=> (int)($right['sort_order'] ?? 0); if ($sortDiff !== 0) { return $sortDiff; } return strcmp((string)($left['content_id'] ?? ''), (string)($right['content_id'] ?? '')); }); $chapters[] = [ 'code' => $chapter['code'], 'name' => $chapter['name'], 'lessons' => array_map(static function ($lesson) { unset($lesson['sort_order']); return $lesson; }, $chapter['lessons']), ]; } echo json_encode([ 'success' => true, 'chapters' => $chapters, ], JSON_UNESCAPED_UNICODE); } catch (Throwable $e) { http_response_code(500); echo json_encode([ 'success' => false, 'message' => 'server_error', ], JSON_UNESCAPED_UNICODE); }