Files
edu/bbs/main_data.php
T

252 lines
11 KiB
PHP

<?php
declare(strict_types=1);
// auth.php and session are already initialized by skin/index.php before this file is included.
$userName = edu_user_field('name', (string) ($_SESSION['member_name'] ?? ''));
$userRank = (string) ($_SESSION['rank_name'] ?? edu_user_field('rank_name', ''));
$memberId = edu_current_member_id();
$sysCompCode = (string) ($_SESSION['sys_comp_code'] ?? '');
require_once __DIR__ . '/db_conn.php';
// 세션에 누락된 정보 DB에서 보완
if (($userRank === '' || $sysCompCode === '') && $memberId !== '') {
try {
if ($sysCompCode !== '') {
$__st = db_conn()->prepare(
'SELECT name, rank_name, sys_comp_code FROM edu_users WHERE member_id = ? AND sys_comp_code = ? LIMIT 1'
);
$__st->execute([$memberId, $sysCompCode]);
} else {
$__st = db_conn()->prepare(
'SELECT name, rank_name, sys_comp_code FROM edu_users WHERE member_id = ? ORDER BY sys_comp_code LIMIT 1'
);
$__st->execute([$memberId]);
}
$__row = $__st->fetch(PDO::FETCH_ASSOC);
if ($__row) {
if ($userName === '')
$userName = (string) ($__row['name'] ?? '');
if ($userRank === '')
$userRank = (string) ($__row['rank_name'] ?? '');
if ($sysCompCode === '')
$sysCompCode = (string) ($__row['sys_comp_code'] ?? '');
}
} catch (Throwable $__e) {
error_log('[main_data rank fallback] ' . $__e->getMessage());
}
}
$userRank = trim($userRank);
// ── 키워드 & 영상 설정 ─────────────────────────────────────────
$allKeywords = []; // 전체 키워드 목록 (모달 선택용, 21개)
$adminKeywords = []; // 회사 추천 키워드 (kw-deny)
$myKeywords = []; // 사용자 선택 키워드 0~3개 (kw-allow)
$videosJson = '[]';
$myKwJson = '[]'; // JS fallback용
$totalMin = 0;
$avgWatchMin = 0; // 전체 평균 학습시간 (분)
$mainDataError = ''; // DEBUG용 오류 메시지
try {
$pdo = db_conn();
$pdo->exec("SET NAMES 'utf8mb4'");
// edu_user_keywords 테이블 자동 생성 (권한 없으면 무시)
try {
$pdo->exec("
CREATE TABLE IF NOT EXISTS edu_user_keywords (
member_id VARCHAR(20) NOT NULL,
sys_comp_code VARCHAR(20) NOT NULL DEFAULT '',
keyword_code VARCHAR(20) NOT NULL,
sort_order INT NOT NULL DEFAULT 0,
PRIMARY KEY (member_id, sys_comp_code, keyword_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
");
} catch (Throwable $__ce) {
error_log('[main_data] CREATE edu_user_keywords: ' . $__ce->getMessage());
}
// 전체 키워드 (모달 선택용) — sort_order 없는 테이블도 안전하게 base_code만으로 정렬
$stmtKw = $pdo->query("SELECT base_code AS keyword_code, code_name AS keyword_name FROM edu_codes WHERE group_code = 'KW100' ORDER BY base_code");
$allKeywords = $stmtKw->fetchAll(PDO::FETCH_ASSOC);
// 회사 추천 키워드: 반드시 edu_recommend_keywords의 코드값을 edu_codes와 한글 매핑해서 사용
try {
$stmtAdm = $pdo->prepare("
SELECT rk.keyword_code, ec.code_name AS keyword_name
FROM edu_recommend_keywords rk
JOIN edu_codes ec ON ec.base_code = rk.keyword_code
WHERE rk.is_active = 1
AND rk.keyword_code IS NOT NULL
AND rk.keyword_code <> ''
AND ec.group_code = 'KW100'
ORDER BY rk.keyword_code
LIMIT 2
");
$stmtAdm->execute();
$adminKeywords = $stmtAdm->fetchAll(PDO::FETCH_ASSOC);
error_log('[main_data] edu_recommend_keywords result: ' . count($adminKeywords) . ' rows');
} catch (Throwable $__ae) {
error_log('[main_data] edu_recommend_keywords 쿼리 실패: ' . $__ae->getMessage());
$adminKeywords = [];
}
// 사용자 선택 키워드 (0~3개)
// ⚠️ sys_comp_code 없어도 저장되도록 조건 완화 (저장 후 바로 조회 위함)
// sys_comp_code 조건 추가 : 권오재
if ($memberId !== '') {
$stmtMy = $pdo->prepare("
SELECT uk.keyword_code, ec.code_name AS keyword_name
FROM edu_user_keywords uk
JOIN edu_codes ec ON ec.base_code = uk.keyword_code
WHERE uk.member_id = ?
AND uk.sys_comp_code = ?
AND uk.sys_comp_code <> ''
ORDER BY uk.keyword_code
LIMIT 3
");
$stmtMy->execute([$memberId, $sysCompCode]);
$myKeywords = $stmtMy->fetchAll(PDO::FETCH_ASSOC);
error_log('[main_data] myKeywords query: memberId=' . $memberId . ', result_count=' . count($myKeywords));
}
// JS fallback용: 사용자 키워드명 배열 (JSON)
$myKwJson = json_encode(
array_column($myKeywords, 'keyword_name'),
JSON_UNESCAPED_UNICODE | JSON_HEX_TAG
);
// 누적 시청 시간 (게이지): watch_tm 기준
if ($memberId !== '' && $sysCompCode !== '') {
$stmtTm = $pdo->prepare(
'SELECT COALESCE(SUM(watch_tm), 0) FROM edu_learning_histories WHERE member_id = ? AND sys_comp_code = ?'
);
$stmtTm->execute([$memberId, $sysCompCode]);
$totalMin = (int) floor((int) $stmtTm->fetchColumn() / 60);
}
// 전체 평균 학습시간 계산: (모든 member_id의 총 watch_tm 합계) / (학습경험이 있는 사용자 수)
try {
$stmtAvg = $pdo->prepare("
SELECT
COALESCE(SUM(h.watch_tm), 0) as total_watch_tm,
COUNT(DISTINCT h.member_id) as unique_members
FROM edu_learning_histories h
WHERE h.sys_comp_code = ?
");
$stmtAvg->execute([$sysCompCode]);
$avgRow = $stmtAvg->fetch(PDO::FETCH_ASSOC);
if ($avgRow) {
$totalWatchTm = (int) ($avgRow['total_watch_tm'] ?? 0);
$uniqueMembers = (int) ($avgRow['unique_members'] ?? 0);
if ($uniqueMembers > 0) {
$avgWatchMin = (int) floor($totalWatchTm / $uniqueMembers / 60);
// 최소 50분으로 설정해서 게이지의 maxValue 보장
$avgWatchMin = max(50, $avgWatchMin);
error_log('[main_data] avgWatchMin calc: totalWatchTm=' . $totalWatchTm . ', uniqueMembers=' . $uniqueMembers . ', avgWatchMin=' . $avgWatchMin);
}
}
} catch (Throwable $__ae) {
error_log('[main_data] avgWatchMin 계산 실패: ' . $__ae->getMessage());
}
// ── Pick 영상(추천) + 키워드 영상 5개 조합: 항상 6개 보장 ──
$videos = [];
// 1. Pick(추천) 영상: is_offer=1, 본인 제외, 랜덤 1개
$stmtPick = $pdo->prepare("SELECT * FROM edu_contents WHERE is_offer=1 AND is_active=1 AND issue_type_code = 'IS10003' AND member_id != ? ORDER BY RAND() LIMIT 1");
$stmtPick->execute([$memberId]);
$pick = $stmtPick->fetch(PDO::FETCH_ASSOC);
if ($pick) {
$pick['is_pick'] = true;
$videos[] = $pick;
}
// 2. 나머지 5개: 실제 사용자 키워드 기반 영상 쿼리로 채움
$keywordVideos = [];
$userKeywordCodes = array_column($myKeywords, 'keyword_code');
if (!empty($userKeywordCodes)) {
$ph = implode(',', array_fill(0, count($userKeywordCodes), '?'));
$sql = "SELECT * FROM edu_contents c
JOIN edu_content_keywords k ON c.content_id = k.content_id AND k.is_active = 1
WHERE c.is_active=1 AND k.keyword_code IN ($ph)";
$params = $userKeywordCodes;
// pick과 중복 제거
if ($pick && isset($pick['content_id'])) {
$sql .= " AND c.content_id != ?";
$params[] = $pick['content_id'];
}
$sql .= " GROUP BY c.content_id ORDER BY c.content_id DESC LIMIT 5";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
$row['is_pick'] = false;
$keywordVideos[] = $row;
}
}
// 부족하면 전체 영상에서 추가로 채움
if (count($keywordVideos) < 5) {
$already = array_column($keywordVideos, 'content_id');
if ($pick && isset($pick['content_id']))
$already[] = $pick['content_id'];
$ph = implode(',', array_fill(0, count($already), '?'));
$sql = "SELECT * FROM edu_contents WHERE is_active=1";
if (!empty($already))
$sql .= " AND content_id NOT IN ($ph)";
$sql .= " ORDER BY content_id DESC LIMIT " . (5 - count($keywordVideos));
$stmt = $pdo->prepare($sql);
$stmt->execute($already);
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
$row['is_pick'] = false;
$keywordVideos[] = $row;
}
}
// 최대 5개만
$keywordVideos = array_slice($keywordVideos, 0, 5);
foreach ($keywordVideos as $v) {
$videos[] = $v;
}
// 3. 부족하면 빈 카드로 채움
while (count($videos) < 6) {
$videos[] = [
'content_id' => 'empty_' . count($videos),
'title' => '',
'is_pick' => false,
'img' => '',
'category' => '',
'author' => '',
'keywords' => [],
];
}
// 카테고리 코드→한글명 매핑 (edu_codes)
$codeMap = [];
$normCode = static function (string $code): string {
return preg_replace('/[^A-Z0-9]/', '', strtoupper(trim($code)));
};
try {
$stmtCm = $pdo->query("SELECT base_code, code_name FROM edu_codes");
foreach ($stmtCm->fetchAll(PDO::FETCH_ASSOC) as $cr) {
$baseCode = $normCode((string) ($cr['base_code'] ?? ''));
if ($baseCode === '')
continue;
$codeMap[$baseCode] = $cr['code_name'];
}
} catch (Throwable $e) { /* ignore */
}
foreach ($videos as &$v) {
if (!is_array($v) || !isset($v['content_id']) || strpos((string) ($v['content_id'] ?? ''), 'empty_') === 0)
continue;
$categoryCode = $normCode((string) ($v['category_code'] ?? ''));
$groupCode = $normCode((string) ($v['category_group'] ?? ''));
$v['category'] = $codeMap[$categoryCode] ?? ($v['category_code'] ?? '');
$v['subcate'] = $codeMap[$groupCode] ?? ($v['category_group'] ?? '');
}
unset($v);
// 4. JSON 직렬화
$videosJson = json_encode($videos, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG);
} catch (Throwable $e) {
$mainDataError = $e->getMessage();
error_log('[main_data] ' . $e->getMessage());
}