539 lines
19 KiB
PHP
539 lines
19 KiB
PHP
<?php
|
|
//=========================
|
|
// 콘텐츠리스트 AJAX
|
|
// [마이페이지][시청중인, 시청완료, 저장한 콘텐츠] 데이터 동적변환
|
|
//=========================
|
|
require_once __DIR__ . '/../bbs/db_conn.php';
|
|
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
|
|
// TODO: 실제 로그인 세션 연동 후 교체
|
|
if (session_status() === PHP_SESSION_NONE) {
|
|
session_start();
|
|
}
|
|
|
|
$memberId = $_SESSION['member_id'] ?? '';
|
|
$sysCompCode = $_SESSION['sys_comp_code'] ?? '';
|
|
//$memberId = 'U001';
|
|
//$sysCompCode = 'COMP01';
|
|
|
|
function mypage_json(array $data): void
|
|
{
|
|
echo json_encode($data, JSON_UNESCAPED_UNICODE);
|
|
exit;
|
|
}
|
|
|
|
function mypage_h(?string $str): string
|
|
{
|
|
return htmlspecialchars((string)$str, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
|
}
|
|
|
|
function map_content_row(array $row, array $catMap = []): array
|
|
{
|
|
//keywords_code_name
|
|
$kwStr = (string)($row['keywords_code_name'] ?? $row['keywords'] ?? '');
|
|
$keywords = ($kwStr !== '') ? explode(',', $kwStr) : [];
|
|
|
|
$watchTm = (float)($row['watch_tm'] ?? 0);
|
|
$contentTm = (float)($row['content_tm'] ?? 0);
|
|
$allTm = (float)($row['all_tm'] ?? 0);
|
|
$gauge = ($contentTm > 0) ? (int)round(($watchTm / $contentTm) * 100) : 0;
|
|
$gauge = max(0, min(100, $gauge));
|
|
|
|
$raw = trim((string)($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((string)($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" : '/img/video/img_thumb_01.png');
|
|
|
|
return [
|
|
'id' => $row['content_id'] ?? '',
|
|
'content_id' => $row['content_id'] ?? '',
|
|
'category_code' => $row['category_code'] ?? '',
|
|
'category_name' => $catMap[$row['category_code'] ?? ''] ?? ($row['category_code'] ?? ''),
|
|
'subcate' => (string)($row['category_group'] ?? ''),
|
|
'title' => $row['title'] ?? '',
|
|
'description' => trim((string)($row['description'] ?? '')),
|
|
'description2' => trim((string)($row['description2'] ?? '')),
|
|
'url' => $raw,
|
|
'content_url' => $raw,
|
|
'video_url' => $url,
|
|
'video_id' => $videoId,
|
|
'thumbnail' => $thumbnail,
|
|
'watch_tm' => (int)($row['watch_tm'] ?? 0),
|
|
'content_tm' => (int)($row['content_tm'] ?? 0),
|
|
'all_tm' => (int)$allTm,
|
|
'gauge' => $gauge,
|
|
'is_bookmarked' => !empty($row['is_bookmarked']),
|
|
'bookmark' => !empty($row['is_bookmarked']),
|
|
'keywords' => $keywords,
|
|
];
|
|
}
|
|
|
|
function category_tag_class(string $categoryCode): string
|
|
{
|
|
$map = [
|
|
'CA10001' => 'myclass',
|
|
'CA10002' => 'onboarding',
|
|
'CA10003' => 'legal',
|
|
'CA10004' => 'leader',
|
|
'CA10005' => 'insight',
|
|
'CA10006' => 'biz',
|
|
];
|
|
|
|
return $map[$categoryCode] ?? 'etc';
|
|
}
|
|
|
|
try {
|
|
$pdo = db_conn();
|
|
$pdo->exec("SET NAMES 'utf8mb4'");
|
|
|
|
$tab = trim((string)($_GET['tab'] ?? 'watching'));
|
|
$page = max(1, (int)($_GET['page'] ?? 1));
|
|
$limit = max(1, (int)($_GET['limit'] ?? 12));
|
|
$offset = ($page - 1) * $limit;
|
|
|
|
// ★ 선택년도 수집
|
|
$selectedYear = trim((string)($_GET['selected_year'] ?? date('Y')));
|
|
if ($selectedYear === '' || !preg_match('/^\d{4}$/', $selectedYear)) {
|
|
$selectedYear = date('Y');
|
|
}
|
|
|
|
$CATEGORY_MAP = [
|
|
'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) {
|
|
$CATEGORY_MAP[$row['base_code']] = $row['code_name'];
|
|
}
|
|
|
|
// =========================
|
|
// 탭 카운트 (선택년도 기준)
|
|
// =========================
|
|
|
|
// 시청중인 콘텐츠 카운트
|
|
/*
|
|
* $stmtCountWatching = $pdo->prepare("
|
|
SELECT COUNT(*)
|
|
FROM edu_learning_histories
|
|
WHERE member_id = :member_id
|
|
AND sys_comp_code = :sys_comp_code
|
|
AND watch_tm < content_tm
|
|
AND YEAR(last_viewed_at) = :selected_year
|
|
");
|
|
*/
|
|
$stmtCountWatching = $pdo->prepare("
|
|
SELECT COUNT(DISTINCT lh.content_id)
|
|
FROM edu_learning_histories lh
|
|
WHERE lh.member_id = :member_id
|
|
AND lh.sys_comp_code = :sys_comp_code
|
|
AND lh.watch_tm < lh.content_tm
|
|
AND YEAR(lh.last_viewed_at) = :selected_year
|
|
AND NOT EXISTS (
|
|
SELECT 1
|
|
FROM edu_learning_histories lh2
|
|
WHERE lh2.content_id = lh.content_id
|
|
AND lh2.member_id = lh.member_id
|
|
AND lh2.sys_comp_code = lh.sys_comp_code
|
|
AND lh2.completed_at IS NOT NULL
|
|
)
|
|
");
|
|
$stmtCountWatching->execute([
|
|
':member_id' => $memberId,
|
|
':sys_comp_code' => $sysCompCode,
|
|
':selected_year' => (int)$selectedYear,
|
|
]);
|
|
$countWatching = (int)$stmtCountWatching->fetchColumn();
|
|
|
|
// 시청완료
|
|
// 기준:
|
|
// 1) completed_at 이 있는 이력만 완료로 인정
|
|
// 2) completed_at 기준 연도 필터
|
|
// 3) 같은 콘텐츠는 최신 완료 1건만 카운트
|
|
$stmtCountCompleted = $pdo->prepare("
|
|
SELECT COUNT(*)
|
|
FROM (
|
|
SELECT
|
|
content_id,
|
|
MAX(completed_at) AS latest_completed_at
|
|
FROM edu_learning_histories
|
|
WHERE member_id = :member_id
|
|
AND sys_comp_code = :sys_comp_code
|
|
AND completed_at IS NOT NULL
|
|
AND YEAR(completed_at) = :selected_year
|
|
GROUP BY content_id
|
|
) t
|
|
");
|
|
$stmtCountCompleted->execute([
|
|
':member_id' => $memberId,
|
|
':sys_comp_code' => $sysCompCode,
|
|
':selected_year' => (int)$selectedYear,
|
|
]);
|
|
$countCompleted = (int)$stmtCountCompleted->fetchColumn();
|
|
|
|
// 저장한 콘텐츠
|
|
// ★ 저장한 시점 기준 년도(favorited_at 우선, 없으면 updated_at, created_at fallback)
|
|
$stmtCountSaved = $pdo->prepare("
|
|
SELECT COUNT(DISTINCT content_id)
|
|
FROM edu_content_wishlist
|
|
WHERE member_id = :member_id
|
|
AND sys_comp_code = :sys_comp_code
|
|
AND is_active = '1'
|
|
AND YEAR(COALESCE(favorited_at, updated_at, created_at)) = :selected_year
|
|
");
|
|
$stmtCountSaved->execute([
|
|
':member_id' => $memberId,
|
|
':sys_comp_code' => $sysCompCode,
|
|
':selected_year' => (int)$selectedYear,
|
|
]);
|
|
$countSaved = (int)$stmtCountSaved->fetchColumn();
|
|
|
|
// =========================
|
|
// 탭별 메인 쿼리
|
|
// =========================
|
|
if ($tab === 'saved') {
|
|
// 저장한 콘텐츠 : wishlist 기준
|
|
$countTotal = $countSaved;
|
|
|
|
$sql = "
|
|
SELECT
|
|
c.content_id,
|
|
c.category_code,
|
|
c.category_group,
|
|
c.title,
|
|
c.description,
|
|
c.description2,
|
|
c.content_url,
|
|
c.thumbnail_url,
|
|
COALESCE(MAX(lh.watch_tm), 0) AS watch_tm,
|
|
COALESCE(MAX(lh.content_tm), 0) AS content_tm,
|
|
COALESCE(MAX(lh.all_tm), 0) AS all_tm,
|
|
1 AS is_bookmarked,
|
|
GROUP_CONCAT(DISTINCT ck.keyword_code ORDER BY ck.keyword_code SEPARATOR ',') AS keywords,
|
|
MAX(lh.last_viewed_at) AS sort_last_viewed_at,
|
|
MAX(COALESCE(cw.favorited_at, cw.updated_at, cw.created_at)) AS sort_saved_at
|
|
FROM edu_content_wishlist cw
|
|
INNER JOIN edu_contents c
|
|
ON c.content_id = cw.content_id
|
|
LEFT JOIN edu_learning_histories lh
|
|
ON lh.content_id = c.content_id
|
|
AND lh.member_id = cw.member_id
|
|
AND lh.sys_comp_code = cw.sys_comp_code
|
|
AND YEAR(lh.last_viewed_at) = :selected_year_lh
|
|
LEFT JOIN edu_content_keywords ck
|
|
ON ck.content_id = c.content_id
|
|
WHERE cw.member_id = :member_id
|
|
AND cw.sys_comp_code = :sys_comp_code
|
|
AND cw.is_active = '1'
|
|
AND YEAR(COALESCE(cw.favorited_at, cw.updated_at, cw.created_at)) = :selected_year_saved
|
|
GROUP BY c.content_id
|
|
ORDER BY
|
|
CASE WHEN MAX(lh.last_viewed_at) IS NULL THEN 1 ELSE 0 END ASC,
|
|
MAX(lh.last_viewed_at) DESC,
|
|
sort_saved_at DESC
|
|
LIMIT :limit OFFSET :offset
|
|
";
|
|
} elseif ($tab === 'completed') {
|
|
// 시청완료 콘텐츠 completed_at IS NOT NULL (최신 1건)
|
|
// 기준:
|
|
// 1) completed_at 이 있는 이력만 완료로 인정
|
|
// 2) 선택년도는 completed_at 기준
|
|
// 3) 같은 콘텐츠가 여러 번 완료된 경우 최신 완료 1건만 반영
|
|
$countTotal = $countCompleted;
|
|
|
|
$sql = "
|
|
SELECT
|
|
c.content_id,
|
|
c.category_code,
|
|
c.category_group,
|
|
c.title,
|
|
c.description,
|
|
c.description2,
|
|
c.content_url,
|
|
c.thumbnail_url,
|
|
lh.watch_tm,
|
|
lh.content_tm,
|
|
lh.all_tm,
|
|
CASE WHEN cw.content_id IS NOT NULL THEN 1 ELSE 0 END AS is_bookmarked,
|
|
GROUP_CONCAT(DISTINCT ck.keyword_code ORDER BY ck.keyword_code SEPARATOR ',') AS keywords,
|
|
lh.completed_at AS sort_completed_at
|
|
FROM (
|
|
SELECT
|
|
t1.content_id,
|
|
t1.member_id,
|
|
t1.sys_comp_code,
|
|
t1.watch_tm,
|
|
t1.content_tm,
|
|
t1.all_tm,
|
|
t1.completed_at
|
|
FROM edu_learning_histories t1
|
|
INNER JOIN (
|
|
SELECT
|
|
content_id,
|
|
MAX(completed_at) AS latest_completed_at
|
|
FROM edu_learning_histories
|
|
WHERE member_id = :completed_member_id_1
|
|
AND sys_comp_code = :completed_sys_comp_code_1
|
|
AND completed_at IS NOT NULL
|
|
AND YEAR(completed_at) = :completed_selected_year_1
|
|
GROUP BY content_id
|
|
) t2
|
|
ON t1.content_id = t2.content_id
|
|
AND t1.completed_at = t2.latest_completed_at
|
|
AND t1.member_id = :completed_member_id_2
|
|
AND t1.sys_comp_code = :completed_sys_comp_code_2
|
|
) lh
|
|
INNER JOIN edu_contents c
|
|
ON c.content_id = lh.content_id
|
|
LEFT JOIN edu_content_wishlist cw
|
|
ON cw.content_id = c.content_id
|
|
AND cw.member_id = lh.member_id
|
|
AND cw.sys_comp_code = lh.sys_comp_code
|
|
AND cw.is_active = '1'
|
|
LEFT JOIN edu_content_keywords ck
|
|
ON ck.content_id = c.content_id
|
|
GROUP BY
|
|
c.content_id,
|
|
c.category_code,
|
|
c.category_group,
|
|
c.title,
|
|
c.description,
|
|
c.description2,
|
|
c.content_url,
|
|
c.thumbnail_url,
|
|
lh.watch_tm,
|
|
lh.content_tm,
|
|
lh.all_tm,
|
|
cw.content_id,
|
|
lh.completed_at
|
|
ORDER BY lh.completed_at DESC
|
|
LIMIT :limit OFFSET :offset
|
|
";
|
|
} else {
|
|
// 시청중인 콘텐츠 : watch_tm < content_tm
|
|
$tab = 'watching';
|
|
$countTotal = $countWatching;
|
|
|
|
$sql = "
|
|
SELECT
|
|
c.content_id,
|
|
c.category_code,
|
|
c.category_group,
|
|
c.title,
|
|
c.description,
|
|
c.description2,
|
|
c.content_url,
|
|
c.thumbnail_url,
|
|
lh.watch_tm,
|
|
lh.content_tm,
|
|
lh.all_tm,
|
|
CASE WHEN cw.content_id IS NOT NULL THEN 1 ELSE 0 END AS is_bookmarked,
|
|
-- 기존 코드 리스트
|
|
GROUP_CONCAT(DISTINCT ck.keyword_code ORDER BY ck.keyword_code SEPARATOR ',') AS keywords,
|
|
-- 추가된 코드명 리스트
|
|
GROUP_CONCAT(DISTINCT ec.code_name ORDER BY ck.keyword_code SEPARATOR ',') AS keywords_code_name
|
|
FROM edu_learning_histories lh
|
|
INNER JOIN edu_contents c
|
|
ON c.content_id = lh.content_id
|
|
LEFT JOIN edu_content_wishlist cw
|
|
ON cw.content_id = c.content_id
|
|
AND cw.member_id = lh.member_id
|
|
AND cw.sys_comp_code = lh.sys_comp_code
|
|
AND cw.is_active = '1'
|
|
LEFT JOIN edu_content_keywords ck
|
|
ON ck.content_id = c.content_id
|
|
-- [추가] 코드 테이블 조인
|
|
LEFT JOIN edu_codes ec
|
|
ON ec.base_code = ck.keyword_code
|
|
AND ec.group_code = 'KW100'
|
|
|
|
WHERE lh.member_id = :member_id
|
|
AND lh.sys_comp_code = :sys_comp_code
|
|
AND lh.watch_tm < lh.content_tm
|
|
AND YEAR(lh.last_viewed_at) = :selected_year
|
|
AND NOT EXISTS (
|
|
SELECT 1
|
|
FROM edu_learning_histories lh2
|
|
WHERE lh2.content_id = lh.content_id
|
|
AND lh2.member_id = lh.member_id
|
|
AND lh2.sys_comp_code = lh.sys_comp_code
|
|
AND lh2.completed_at IS NOT NULL
|
|
)
|
|
GROUP BY c.content_id
|
|
ORDER BY lh.last_viewed_at DESC
|
|
LIMIT :limit OFFSET :offset
|
|
|
|
";
|
|
|
|
/*
|
|
WHERE lh.member_id = :member_id
|
|
AND lh.sys_comp_code = :sys_comp_code
|
|
AND lh.watch_tm < lh.content_tm
|
|
AND YEAR(lh.last_viewed_at) = :selected_year
|
|
GROUP BY c.content_id
|
|
ORDER BY lh.last_viewed_at DESC
|
|
LIMIT :limit OFFSET :offset
|
|
";*/
|
|
|
|
// $sql = "
|
|
// SELECT
|
|
// c.content_id,
|
|
// c.category_code,
|
|
// c.title,
|
|
// c.content_url,
|
|
// c.thumbnail_url,
|
|
// lh.watch_tm,
|
|
// lh.content_tm,
|
|
// CASE WHEN cw.content_id IS NOT NULL THEN 1 ELSE 0 END AS is_bookmarked,
|
|
// GROUP_CONCAT(DISTINCT ck.keyword_code ORDER BY ck.keyword_code SEPARATOR ',') AS keywords
|
|
// FROM edu_learning_histories lh
|
|
// INNER JOIN edu_contents c
|
|
// ON c.content_id = lh.content_id
|
|
// LEFT JOIN edu_content_wishlist cw
|
|
// ON cw.content_id = c.content_id
|
|
// AND cw.member_id = lh.member_id
|
|
// AND cw.sys_comp_code = lh.sys_comp_code
|
|
// AND cw.is_active = '1'
|
|
// LEFT JOIN edu_content_keywords ck
|
|
// ON ck.content_id = c.content_id
|
|
// WHERE lh.member_id = :member_id
|
|
// AND lh.sys_comp_code = :sys_comp_code
|
|
// AND lh.watch_tm < lh.content_tm
|
|
// AND YEAR(lh.last_viewed_at) = :selected_year
|
|
// GROUP BY c.content_id
|
|
// ORDER BY lh.last_viewed_at DESC
|
|
// LIMIT :limit OFFSET :offset
|
|
// ";
|
|
}
|
|
|
|
|
|
|
|
$stmt = $pdo->prepare($sql);
|
|
|
|
if ($tab === 'saved') {
|
|
$stmt->bindValue(':member_id', $memberId, PDO::PARAM_STR);
|
|
$stmt->bindValue(':sys_comp_code', $sysCompCode, PDO::PARAM_STR);
|
|
$stmt->bindValue(':selected_year_lh', (int)$selectedYear, PDO::PARAM_INT);
|
|
$stmt->bindValue(':selected_year_saved', (int)$selectedYear, PDO::PARAM_INT);
|
|
|
|
} elseif ($tab === 'completed') {
|
|
$stmt->bindValue(':completed_member_id_1', $memberId, PDO::PARAM_STR);
|
|
$stmt->bindValue(':completed_sys_comp_code_1', $sysCompCode, PDO::PARAM_STR);
|
|
$stmt->bindValue(':completed_selected_year_1', (int)$selectedYear, PDO::PARAM_INT);
|
|
$stmt->bindValue(':completed_member_id_2', $memberId, PDO::PARAM_STR);
|
|
$stmt->bindValue(':completed_sys_comp_code_2', $sysCompCode, PDO::PARAM_STR);
|
|
|
|
} else {
|
|
$stmt->bindValue(':member_id', $memberId, PDO::PARAM_STR);
|
|
$stmt->bindValue(':sys_comp_code', $sysCompCode, PDO::PARAM_STR);
|
|
$stmt->bindValue(':selected_year', (int)$selectedYear, PDO::PARAM_INT);
|
|
}
|
|
|
|
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
|
|
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
|
|
$stmt->execute();
|
|
|
|
|
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
ob_start();
|
|
$videos = [];
|
|
|
|
foreach ($rows as $row) {
|
|
$item = map_content_row($row, $CATEGORY_MAP);
|
|
$tagClass = category_tag_class($item['category_code']);
|
|
$videos[] = $item;
|
|
|
|
|
|
$item['url']="#"; //마이페이지용 모달 개발전까지 링크삭제
|
|
|
|
?>
|
|
<li title="클릭 시, 모달 연결은 추후 개발예정"
|
|
class="content-card <?php if ($tab === 'completed') { ?>content-card--completed<?php } ?>"
|
|
data-content-id="<?= mypage_h($item['content_id']) ?>"
|
|
>
|
|
<a href="<?= mypage_h($item['url']) ?>" class="card-link">
|
|
<label class="bookmark" for="wish_<?= mypage_h($tab . '_' . $item['content_id']) ?>" onclick="event.stopPropagation();">
|
|
<input
|
|
type="checkbox"
|
|
id="wish_<?= mypage_h($tab . '_' . $item['content_id']) ?>"
|
|
title="저장"
|
|
<?php if ($item['is_bookmarked']) { ?>checked<?php } ?>
|
|
>
|
|
</label>
|
|
|
|
<div class="item-thumb">
|
|
<img src="<?= mypage_h($item['thumbnail']) ?>" alt="<?= mypage_h($item['title']) ?>">
|
|
<div class="gauge-bar rect">
|
|
<div class="gauge-fill" style="width: <?= (int)$item['gauge'] ?>%"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="item-info">
|
|
<span class="category-tag <?= mypage_h($tagClass) ?>">
|
|
<?= mypage_h($item['category_name']) ?>
|
|
</span>
|
|
<strong class="item-title"><?= mypage_h($item['title']) ?></strong>
|
|
|
|
<div class="tag-list">
|
|
<?php foreach ($item['keywords'] as $kw): ?>
|
|
<?php if (trim((string)$kw) === '') { continue; } ?>
|
|
<span class="tag"><?= mypage_h($kw) ?></span>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
|
|
<button type="button" class="btn-remove-card" title="삭제"></button>
|
|
</div>
|
|
</a>
|
|
</li>
|
|
<?php
|
|
}
|
|
|
|
$html = ob_get_clean();
|
|
|
|
mypage_json([
|
|
'success' => true,
|
|
'tab' => $tab,
|
|
'page' => $page,
|
|
'limit' => $limit,
|
|
'selected_year' => $selectedYear,
|
|
'total_count' => $countTotal,
|
|
'counts' => [
|
|
'watching' => $countWatching,
|
|
'completed' => $countCompleted,
|
|
'saved' => $countSaved,
|
|
],
|
|
'videos' => $videos,
|
|
'html' => $html,
|
|
'is_last' => (($offset + $limit) >= $countTotal),
|
|
]);
|
|
} catch (Throwable $e) {
|
|
mypage_json([
|
|
'success' => false,
|
|
'message' => '콘텐츠 리스트를 불러오지 못했습니다.',
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
}
|