Initial commit: 교육 프로젝트 배포

This commit is contained in:
송대일
2026-07-01 18:32:42 +09:00
commit be6dccd120
1483 changed files with 5082202 additions and 0 deletions
+261
View File
@@ -0,0 +1,261 @@
<?php
declare(strict_types=1);
header('Content-Type: application/json; charset=utf-8');
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
require_once __DIR__ . '/../db_conn.php';
$memberId = (string)($_SESSION['member_id'] ?? '');
$sysCompCode = (string)($_SESSION['sys_comp_code'] ?? '');
if ($memberId === '') {
http_response_code(401);
echo json_encode(['success' => false, 'message' => 'not_logged_in'], JSON_UNESCAPED_UNICODE);
exit;
}
$contentId = trim((string)($_GET['content_id'] ?? ''));
if ($contentId === '') {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'content_id_required'], JSON_UNESCAPED_UNICODE);
exit;
}
try {
$pdo = db_conn();
$normCode = static function (string $code): string {
return preg_replace('/[^A-Z0-9]/', '', strtoupper(trim($code)));
};
$looksLikeCode = static function (string $value): bool {
return preg_match('/^[A-Z]{2}[A-Z0-9]{3,}$/', strtoupper(trim($value))) === 1;
};
$codeMap = [];
try {
$stmtCodeMap = $pdo->query("SELECT base_code, code_name FROM edu_codes");
foreach ($stmtCodeMap->fetchAll(PDO::FETCH_ASSOC) as $cr) {
$key = $normCode((string)($cr['base_code'] ?? ''));
if ($key === '') continue;
$codeMap[$key] = (string)($cr['code_name'] ?? '');
}
} catch (Throwable $ignore) {
$codeMap = [];
}
// 1) 현재 영상의 키워드 코드 조회
$keywordCodes = [];
try {
$stmtKw = $pdo->prepare('SELECT keyword_code FROM edu_content_keywords WHERE content_id = ?');
$stmtKw->execute([$contentId]);
$keywordCodes = $stmtKw->fetchAll(PDO::FETCH_COLUMN) ?: [];
} catch (Throwable $kwErr) {
// edu_content_keywords 테이블이 없는 경우 무시 → fallback
$keywordCodes = [];
}
// 키워드 없으면 같은 카테고리 영상으로 fallback
if (empty($keywordCodes)) {
// 현재 영상의 카테고리 조회
$stmtCat = $pdo->prepare('SELECT category_code FROM edu_contents WHERE content_id = ? LIMIT 1');
$stmtCat->execute([$contentId]);
$curCatCode = $stmtCat->fetchColumn();
if (!$curCatCode) {
echo json_encode([
'success' => true,
'keywords' => [],
'videos' => [],
], JSON_UNESCAPED_UNICODE);
exit;
}
$stmtFallback = $pdo->prepare(
"SELECT c.content_id, c.title, c.content_url, c.thumbnail_url,
c.category_code, c.category_group, c.description,
COALESCE(ec.code_name, c.category_code) AS category_name,
COALESCE(ec_grp.code_name, c.category_group) AS category_group_name
FROM edu_contents c
LEFT JOIN edu_codes ec ON TRIM(UPPER(ec.base_code)) = TRIM(UPPER(c.category_code))
LEFT JOIN edu_codes ec_grp ON TRIM(UPPER(ec_grp.base_code)) = TRIM(UPPER(c.category_group))
WHERE c.category_code = ?
AND c.content_id != ?
AND c.is_active = 1
ORDER BY c.start_date DESC
LIMIT 8"
);
$stmtFallback->execute([$curCatCode, $contentId]);
$rows = $stmtFallback->fetchAll(PDO::FETCH_ASSOC) ?: [];
$videos = [];
foreach ($rows as $row) {
$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;
}
$thumbFromDb = trim($row['thumbnail_url'] ?? '');
$thumbnail = $thumbFromDb !== ''
? $thumbFromDb
: ($videoId !== '' ? "https://img.youtube.com/vi/{$videoId}/sddefault.jpg" : '');
$catCode = $row['category_code'] ?? '';
$subcate = trim((string)($row['category_group_name'] ?? $row['category_group'] ?? ''));
$catName = trim((string)($row['category_name'] ?? $catCode));
if (($catName === '' || $looksLikeCode($catName)) && $catCode !== '') {
$mapped = $codeMap[$normCode($catCode)] ?? '';
if ($mapped !== '') $catName = $mapped;
}
$groupRaw = trim((string)($row['category_group'] ?? ''));
if (($subcate === '' || $looksLikeCode($subcate)) && $groupRaw !== '') {
$mapped = $codeMap[$normCode($groupRaw)] ?? '';
if ($mapped !== '') $subcate = $mapped;
}
$videos[] = [
'content_id' => $row['content_id'],
'title' => $row['title'] ?? '',
'content_url' => $videoId !== '' ? "https://www.youtube.com/watch?v={$videoId}" : $raw,
'thumbnail' => $thumbnail,
'category_code' => $catCode,
'category' => $catName,
'category_name' => $catName,
'subcate' => $subcate,
'description' => $row['description'] ?? '',
];
}
echo json_encode([
'success' => true,
'keywords' => [],
'videos' => $videos,
], JSON_UNESCAPED_UNICODE);
exit;
}
// 2) 키워드명 조회 (표시용)
$kwPlaceholders = implode(',', array_fill(0, count($keywordCodes), '?'));
$keywordNames = [];
try {
$stmtKwNames = $pdo->prepare(
"SELECT keyword_code, keyword_name FROM edu_keywords WHERE keyword_code IN ({$kwPlaceholders})"
);
$stmtKwNames->execute($keywordCodes);
while ($row = $stmtKwNames->fetch(PDO::FETCH_ASSOC)) {
$keywordNames[] = $row['keyword_name'];
}
} catch (Throwable $kwNameErr) {
$keywordNames = [];
}
// 3) 동일 키워드를 가진 다른 영상 조회 (현재 영상 제외, 랜덤 8개)
$rows = [];
try {
$stmtVideos = $pdo->prepare(
"SELECT DISTINCT
c.content_id,
c.title,
c.content_url,
c.thumbnail_url,
c.category_code,
c.category_group,
c.description,
COALESCE(ec.code_name, c.category_code) AS category_name,
COALESCE(ec_grp.code_name, c.category_group) AS category_group_name
FROM edu_contents c
LEFT JOIN edu_codes ec ON TRIM(UPPER(ec.base_code)) = TRIM(UPPER(c.category_code))
LEFT JOIN edu_codes ec_grp ON TRIM(UPPER(ec_grp.base_code)) = TRIM(UPPER(c.category_group))
INNER JOIN edu_content_keywords ck ON ck.content_id = c.content_id
WHERE ck.keyword_code IN ({$kwPlaceholders})
AND c.content_id != ?
AND c.is_active = 1
ORDER BY RAND()
LIMIT 8"
);
$params = array_merge($keywordCodes, [$contentId]);
$stmtVideos->execute($params);
$rows = $stmtVideos->fetchAll(PDO::FETCH_ASSOC) ?: [];
} catch (Throwable $kwVideoErr) {
$rows = [];
}
// 키워드 매칭 결과 없으면 같은 카테고리 fallback
if (empty($rows)) {
$stmtCat = $pdo->prepare('SELECT category_code FROM edu_contents WHERE content_id = ? LIMIT 1');
$stmtCat->execute([$contentId]);
$curCatCode = $stmtCat->fetchColumn();
if ($curCatCode) {
$stmtFb = $pdo->prepare(
"SELECT c.content_id, c.title, c.content_url, c.thumbnail_url,
c.category_code, c.category_group, c.description,
COALESCE(ec.code_name, c.category_code) AS category_name,
COALESCE(ec_grp.code_name, c.category_group) AS category_group_name
FROM edu_contents c
LEFT JOIN edu_codes ec ON TRIM(UPPER(ec.base_code)) = TRIM(UPPER(c.category_code))
LEFT JOIN edu_codes ec_grp ON TRIM(UPPER(ec_grp.base_code)) = TRIM(UPPER(c.category_group))
WHERE c.category_code = ?
AND c.content_id != ?
AND c.is_active = 1
ORDER BY c.start_date DESC
LIMIT 8"
);
$stmtFb->execute([$curCatCode, $contentId]);
$rows = $stmtFb->fetchAll(PDO::FETCH_ASSOC) ?: [];
}
}
$videos = [];
foreach ($rows as $row) {
$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;
}
$thumbFromDb = trim($row['thumbnail_url'] ?? '');
$thumbnail = $thumbFromDb !== ''
? $thumbFromDb
: ($videoId !== '' ? "https://img.youtube.com/vi/{$videoId}/sddefault.jpg" : '');
$catCode = $row['category_code'] ?? '';
$subcate = trim((string)($row['category_group_name'] ?? $row['category_group'] ?? ''));
$catName = trim((string)($row['category_name'] ?? $catCode));
if (($catName === '' || $looksLikeCode($catName)) && $catCode !== '') {
$mapped = $codeMap[$normCode($catCode)] ?? '';
if ($mapped !== '') $catName = $mapped;
}
$groupRaw = trim((string)($row['category_group'] ?? ''));
if (($subcate === '' || $looksLikeCode($subcate)) && $groupRaw !== '') {
$mapped = $codeMap[$normCode($groupRaw)] ?? '';
if ($mapped !== '') $subcate = $mapped;
}
$videos[] = [
'content_id' => $row['content_id'],
'title' => $row['title'] ?? '',
'content_url' => $videoId !== '' ? "https://www.youtube.com/watch?v={$videoId}" : $raw,
'thumbnail' => $thumbnail,
'category_code' => $catCode,
'category' => $catName,
'category_name' => $catName,
'subcate' => $subcate,
'description' => $row['description'] ?? '',
];
}
echo json_encode([
'success' => true,
'keywords' => $keywordNames,
'videos' => $videos,
], JSON_UNESCAPED_UNICODE);
} catch (Throwable $e) {
error_log('[get_recommend_videos] ' . $e->getMessage());
http_response_code(500);
echo json_encode(['success' => false, 'message' => 'server_error'], JSON_UNESCAPED_UNICODE);
}