prepare('SELECT sys_comp_code FROM edu_users WHERE member_id = ? ORDER BY sys_comp_code LIMIT 1'); $__st->execute([$memberId]); $sysCompCode = (string)($__st->fetchColumn() ?: ''); } catch (Throwable $__e) { /* ignore */ } } // --------------------------------------------------------- // 디버그 설정 // true : 바인딩 적용된 SQL 출력 후 종료 // false : 정상 실행 // --------------------------------------------------------- //https://baroncs.co.kr/ajax/get_video_list.php?SET_PREFIX=I $DEBUG_SQL = false;//정상진행 //$DEBUG_SQL = true; //디버깅 $prefixCondition = isset($SET_PREFIX) ? $SET_PREFIX : 'L'; //L=리더십(기본값), I=인사이트 // --------------------------------------------------------- // 요청 파라미터 // --------------------------------------------------------- $page = isset($_GET['page']) ? max(1, (int)$_GET['page']) : 1; $limitParam = trim((string)($_GET['limit'] ?? '')); $limit = 8; $noLimit = false; if ($limitParam !== '') { if (strtolower($limitParam) === 'all') { $noLimit = true; } else { $parsedLimit = (int)$limitParam; if ($parsedLimit > 0) { $limit = $parsedLimit; } } } $offset = ($page - 1) * $limit; $category = trim($_GET['category'] ?? ''); // 예: CA200L01 $sort = trim($_GET['sort'] ?? 'view'); // view / latest / seen / unseen // --------------------------------------------------------- // 기본 코드맵 (DB코드 조회 실패 대비) // --------------------------------------------------------- if($prefixCondition=="L"){ $SET_category_code = "CA10004";//리더십 $CATEGORY_MAP = [ 'CA10004' => '리더십', ]; $SUBCATE_MAP = [ 'CA200L01' => '리더십 시작하기', 'CA200L02' => '셀프 리더십', 'CA200L03' => '팀 리더십', 'CA200L04' => '실전 리더십', 'CA200L05' => '인물탐구', ]; }else if($prefixCondition=="I"){ $SET_category_code = "CA10005";//인사이트 $CATEGORY_MAP = [ 'CA10005' => '인사이트', ]; $SUBCATE_MAP = [ 'CA200I01' => '경제와 사회', 'CA200I02' => '기술과 미래', 'CA200I03' => '트렌드 업', 'CA200I04' => '행복과 건강', 'CA200I05' => '피플스토리', 'CA200I06' => '라이프', ]; } // --------------------------------------------------------- // 공통 함수 // --------------------------------------------------------- function FN_change_entity(?string $str): string { return htmlspecialchars((string)$str, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); } function mapContentRow(array $row, array $catMap = [], array $subcateMap = []): array { $kwStr = $row['keyword_names'] ?? ($row['keywords'] ?? ''); $keywords = $kwStr !== '' ? explode(',', $kwStr) : []; $keywords = array_values(array_filter(array_map('trim', $keywords), static function ($v) { return $v !== ''; })); $watchTm = (float)($row['watch_tm'] ?? 0); $contentTm = (float)($row['content_tm'] ?? 0); $gauge = ($contentTm > 0) ? (int)round(($watchTm / $contentTm) * 100) : 0; $gauge = min(100, max(0, $gauge)); $raw = trim($row['content_url'] ?? ''); 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; } else { $videoId = ''; } $thumbFromDb = trim($row['thumbnail_url'] ?? ''); $url = $videoId !== '' ? "https://www.youtube.com/watch?v={$videoId}" : $raw; $thumbnail = $thumbFromDb !== '' ? $thumbFromDb : ($videoId !== '' ? "https://img.youtube.com/vi/{$videoId}/sddefault.jpg" : ''); $categoryCode = $row['category_code'] ?? ''; $groupCode = $row['category_group'] ?? ''; return [ 'id' => $row['content_id'] ?? '', 'url' => $url, 'thumbnail' => $thumbnail, 'category' => $catMap[$categoryCode] ?? $categoryCode, 'category_code' => $categoryCode, 'subcate' => $subcateMap[$groupCode] ?? $groupCode, 'bookmark' => !empty($row['is_bookmarked']), 'title' => $row['title'] ?? '', 'description' => $row['description'] ?? '', 'keywords' => $keywords, 'gauge' => $gauge, 'watch_tm' => (int)($row['watch_tm'] ?? 0), 'content_tm' => (int)($row['content_tm'] ?? 0), 'view_cnt' => (int)($row['view_cnt'] ?? 0), ]; } /** * 디버그용: 바인딩값을 SQL에 치환한 문자열 생성 * 주의: 실제 실행 SQL을 PDO가 제공하는 것은 아니고, 보기 쉽게 만든 디버그용 문자열임 */ function buildDebugSql(string $sql, array $params): string { // :member_id 와 :member_id2 같은 이름 충돌 방지 uksort($params, function ($a, $b) { return strlen((string)$b) <=> strlen((string)$a); }); foreach ($params as $key => $value) { if ($value === null) { $replace = 'NULL'; } elseif (is_int($value) || is_float($value)) { $replace = (string)$value; } else { $replace = "'" . str_replace("'", "''", (string)$value) . "'"; } $sql = str_replace($key, $replace, $sql); } return $sql; } try { $pdo = db_conn(); $pdo->exec("SET NAMES 'utf8mb4'"); // --------------------------------------------------------- // edu_codes 에서 실제 코드맵 조회 // --------------------------------------------------------- $stmtCode = $pdo->query(" SELECT group_code, code, code_name FROM edu_codes WHERE is_active = 1 AND ( group_code = 'CA100' OR ( group_code = 'CA200' AND code LIKE '".$prefixCondition."%' ) ) "); if ($stmtCode) { foreach ($stmtCode->fetchAll(PDO::FETCH_ASSOC) as $cr) { $key = $cr['group_code'] . $cr['code']; if ($cr['group_code'] === 'CA100') { $CATEGORY_MAP[$key] = $cr['code_name']; } elseif ($cr['group_code'] === 'CA200') { $SUBCATE_MAP[$key] = $cr['code_name']; } } } // --------------------------------------------------------- // 이 페이지는 리더십 대분류 전용 // 탭은 category_group 으로 필터링 // category 파라미터는 CA200L01 형태를 권장 // --------------------------------------------------------- $categoryGroup = ''; if ($category !== '' && strtolower($category) !== 'all') { if (preg_match('/^L\d{2}$/', $category)) { $categoryGroup = 'CA200' . $category; // L01 -> CA200L01 } else { $categoryGroup = $category; // 이미 CA200L01이면 그대로 } } // --------------------------------------------------------- // 정렬 조건 // --------------------------------------------------------- switch ($sort) { case 'latest': $orderBy = " c.created_at DESC, c.content_id DESC "; break; case 'seen': $orderBy = " c.created_at DESC, CASE WHEN lh.content_id IS NOT NULL THEN 0 ELSE 1 END ASC, lh.last_viewed_at DESC, c.content_id DESC "; break; case 'unseen': $orderBy = " c.created_at DESC, CASE WHEN lh.content_id IS NULL THEN 0 ELSE 1 END ASC, COALESCE(c.updated_at, c.created_at) DESC, c.content_id DESC "; break; case 'view': default: $orderBy = " COALESCE(vs.view_cnt, 0) DESC, COALESCE(vs.total_all_tm, 0) DESC, c.created_at DESC, c.content_id DESC "; break; } // --------------------------------------------------------- // 기본 조건 // --------------------------------------------------------- $where = []; $bind = []; //배열추가 $where[] = "c.is_active = '1'"; $where[] = "(c.start_date IS NULL OR c.start_date <= CURDATE())"; $where[] = "(c.end_date IS NULL OR c.end_date >= CURDATE())"; // 리더십 대분류 고정 $where[] = "c.category_code = '{$SET_category_code}'"; //edu_codes.base_code == edu_contents.category_code // 탭 필터 if ($categoryGroup !== '') { $where[] = "c.category_group = :category_group"; $bind[':category_group'] = $categoryGroup; } $whereSql = implode("\n AND ", $where);//배열 사이에 구분자를 넣어서 연결 // --------------------------------------------------------- // TOTAL COUNT // --------------------------------------------------------- $countSql = " SELECT COUNT(*) FROM edu_contents c WHERE {$whereSql} "; $stmtCount = $pdo->prepare($countSql); foreach ($bind as $key => $value) { $stmtCount->bindValue($key, $value, PDO::PARAM_STR); } if ($DEBUG_SQL) { $countDebugSql = buildDebugSql($countSql, $bind); echo '
';
        echo "[COUNT SQL]\n\n";
      //  echo htmlspecialchars($countDebugSql, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
        echo $countDebugSql;
        echo "\n\n------------------------------\n\n";
    }
    
    $stmtCount->execute();
    $totalCount = (int)$stmtCount->fetchColumn();
    
    // ---------------------------------------------------------
    // 목록 조회
    // ---------------------------------------------------------
    $limitSql = $noLimit ? '' : "\n        LIMIT :limit OFFSET :offset\n";

    $sql = "
        SELECT
            c.content_id,
            c.category_code,
            c.category_group,
            c.title,
            c.content_url,
            c.thumbnail_url,
            c.description,
            c.sort_order,
            c.created_at,
            c.updated_at,
            
            lh.content_id AS lh_content_id,
            lh.watch_tm,
            lh.content_tm,
            lh.last_viewed_at,
            
            CASE
                WHEN cw.content_id IS NOT NULL THEN 1
                ELSE 0
            END AS is_bookmarked,
            
            COALESCE(vs.view_cnt, 0) AS view_cnt,
            COALESCE(vs.total_all_tm, 0) AS total_all_tm,
            
            GROUP_CONCAT(
                DISTINCT ck.keyword_code
                ORDER BY ck.keyword_code
                SEPARATOR ','
            ) AS keywords,

            GROUP_CONCAT(
                DISTINCT COALESCE(kc.code_name, ck.keyword_code)
                ORDER BY ck.keyword_code
                SEPARATOR ','
            ) AS keyword_names
            
        FROM edu_contents c
        
        LEFT JOIN edu_learning_histories lh
               ON lh.content_id = c.content_id
              AND lh.member_id = :member_id
              AND lh.sys_comp_code = :sys_comp_code
              
        LEFT JOIN edu_content_wishlist cw
               ON cw.content_id = c.content_id
              AND cw.member_id = :member_id2
              AND cw.sys_comp_code = :sys_comp_code2
              AND cw.is_active = '1'
              
        LEFT JOIN (
            SELECT
                content_id,
                COUNT(DISTINCT member_id) AS view_cnt,
                COALESCE(SUM(all_tm), 0) AS total_all_tm
            FROM edu_learning_histories
            GROUP BY content_id
        ) vs
               ON vs.content_id = c.content_id
               
        LEFT JOIN edu_content_keywords ck
               ON ck.content_id = c.content_id
              AND ck.is_active = '1'

          LEFT JOIN edu_codes kc
               ON kc.base_code = ck.keyword_code
              AND kc.is_active = 1
               
        WHERE {$whereSql}
        
        GROUP BY c.content_id
        
        ORDER BY {$orderBy}

        {$limitSql}

    ";
    
    /*LIMIT :limit OFFSET :offset*/
    $stmt = $pdo->prepare($sql);
    
    $stmt->bindValue(':member_id', $memberId, PDO::PARAM_STR);
    $stmt->bindValue(':sys_comp_code', $sysCompCode, PDO::PARAM_STR);
    $stmt->bindValue(':member_id2', $memberId, PDO::PARAM_STR);
    $stmt->bindValue(':sys_comp_code2', $sysCompCode, PDO::PARAM_STR);
    
    foreach ($bind as $key => $value) {
        $stmt->bindValue($key, $value, PDO::PARAM_STR);
    }
    
    if (!$noLimit) {
        $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
        $stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
    }
    
    if ($DEBUG_SQL) {
        $debugParams = [
            ':member_id'      => $memberId,
            ':sys_comp_code'  => $sysCompCode,
            ':member_id2'     => $memberId,
            ':sys_comp_code2' => $sysCompCode,
        ];

        if (!$noLimit) {
            $debugParams[':limit'] = $limit;
            $debugParams[':offset'] = $offset;
        }
        
        if (!empty($bind)) {
            $debugParams = array_merge($debugParams, $bind);
        }
        
        $debugSql = buildDebugSql($sql, $debugParams);
        
        echo "[LIST SQL]\n\n";
        //echo htmlspecialchars($debugSql, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
        echo $debugSql;
        echo '
'; } $stmt->execute(); $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); if ($DEBUG_SQL) { print_r($rows); exit; } // --------------------------------------------------------- // HTML 조각 생성 // --------------------------------------------------------- ob_start(); foreach ($rows as $row) { $item = mapContentRow($row, $CATEGORY_MAP, $SUBCATE_MAP); $contentId = FN_change_entity($item['id']); $title = FN_change_entity($item['title']); $thumb = FN_change_entity($item['thumbnail']); $categoryNm= FN_change_entity($item['category']); $subcateNm = FN_change_entity($item['subcate']); $descRaw = $item['description'] ?? ''; $keywords = $item['keywords'] ?? []; $keywordsCsv = implode(',', $keywords); $keywordTags = ''; if (!empty($keywords)) { $keywordTags = implode(' ', array_map(static function ($kw) { return '#' . $kw; }, $keywords)); } $itemDesc = $keywordTags !== '' ? FN_change_entity($keywordTags) : ($subcateNm !== '' ? $subcateNm : $categoryNm); $checked = $item['bookmark'] ? ' checked' : ''; $viewUrl = "/video_view.php?content_id=" . rawurlencode($item['id']); ?>
  • <?= $title ?> <?= $title ?>
    시청수
  • true, 'html' => $html, 'total_count' => $totalCount ], JSON_UNESCAPED_UNICODE); } catch (Throwable $e) { error_log('[get_video_list.php] ' . $e->getMessage()); echo json_encode([ 'success' => false, 'html' => '', 'total_count' => 0 ], JSON_UNESCAPED_UNICODE); }