Files
edu/bbs/legal_learning_data.php

243 lines
8.8 KiB
PHP

<?php
/**
* bbs/legal_learning_data.php — 법정교육 페이지 데이터
*
* 제공 변수:
* $userName - 사용자 이름
* $userRank - 직위/직급
* $dDay - D-day 숫자
* $deadlineStr - 마감일 문자열 (n월 j일)
* $chapters - 챕터 배열 (id, code, name, lessons[])
* $chaptersJson - $chapters JSON 문자열 (window.learningChapterData)
* $configJson - window.learningConfigData 형식 JSON
* $progressRate - 전체 진도율 (0~100 정수)
*/
require_once __DIR__ . '/db_conn.php';
require_once __DIR__ . '/api/common.php';
$pdo = db_conn();
// ── 1. 로그인 유저 ID (로그인 세션 변수 사용) ────────────────────────────
$sessionUser = api_get_session_user();
if (!$sessionUser || empty($sessionUser['member_id'])) {
// 로그인 세션이 없으면 접근 불가 또는 리다이렉트 처리 (여기선 예시로 403)
http_response_code(403);
exit('로그인 세션이 필요합니다.');
}
$memberId = $sessionUser['member_id'];
$sysCompCode = $sessionUser['sys_comp_code'];
// ── 2. 유저 정보 ─────────────────────────────────────────────────────────
$stmtUser = $pdo->prepare("
SELECT name, rank_name, sys_comp_code
FROM edu_users
WHERE member_id = ?
AND sys_comp_code = ?
LIMIT 1
");
$stmtUser->execute([$memberId, $sysCompCode]);
$userRow = $stmtUser->fetch();
$userName = $userRow['name'] ?? '사용자';
$userRank = $userRow['rank_name'] ?? '';
// ── 3. 법정교육 카테고리/챕터 고정 매핑 (관리자 코드관리 기준) ───────────────
$fixedLegalNameMap = [
'C01' => '개인정보보호',
'C02' => '직장내 괴롭힘 예방',
'C03' => '장애인 인식 개선',
'C04' => '성희롱 예방 교육',
'C05' => '퇴직금 교육',
// 'C06' => '산업안전보건',
];
$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));
// 카테고리명 조회 (edu_codes 테이블)
$phCodes = implode(',', array_fill(0, count($legalRawCodes), '?'));
$stmtCodes = $pdo->prepare("
SELECT base_code, code_name
FROM edu_codes
WHERE base_code IN ($phCodes)
ORDER BY base_code
");
$stmtCodes->execute($legalRawCodes);
$codeNameMap = [];
foreach ($stmtCodes->fetchAll() as $row) {
$codeNameMap[$row['base_code']] = $row['code_name'];
}
// ── 4. 콘텐츠 + 시청 이력 조회 ───────────────────────────────────────────
$stmtContents = $pdo->prepare("
SELECT
c.content_id,
c.title,
c.content_url,
c.description,
c.category_group,
c.end_date,
c.sort_order,
COALESCE(lh.content_tm, 0) AS content_tm,
COALESCE(lh.watch_tm, 0) AS watch_tm,
COALESCE(lh.all_tm, 0) AS 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.is_active = 1
AND c.category_code = 'CA10003'
AND c.category_group IN ($phCodes)
ORDER BY c.category_group, c.sort_order ASC, c.content_id ASC
");
$stmtContents->execute(array_merge([$memberId, $sysCompCode], $legalRawCodes));
$rows = $stmtContents->fetchAll();
// ── 5. 디버그 정보 준비 ──────────────────────────────────────────────
$debugData = [
'WHERE_category_code' => 'CA10003',
'WHERE_category_group_IN' => $legalRawCodes,
'rows_count' => count($rows),
'chapter_configs' => [],
];
foreach ($legalCodes as $code) {
$debugData['chapter_configs'][$code] = [
'normalize_from' => [$code, 'CA200' . $code],
];
}
// ── 6. 챕터별 그룹화 ─────────────────────────────────────────────────────
$chapterMap = [];
foreach ($legalCodes as $idx => $code) {
$chapterMap[$code] = [
'id' => $idx + 1,
'code' => $code,
'name' => $fixedLegalNameMap[$code] ?? ($codeNameMap[$code] ?? $code),
'lessons' => [],
'completed' => true, // 아래에서 미완료 레슨 있으면 false
'min_end_date' => null,
];
}
foreach ($rows as $row) {
$code = $normalizeLegalGroupCode($row['category_group'] ?? '');
if ($code === '' || !isset($chapterMap[$code])) { continue; }
$contentTm = (int)$row['content_tm'];
$watchTm = (int)$row['watch_tm'];
$allTm = (int)$row['all_tm'];
$completedAt = $row['completed_at'];
$hasHistory = ($contentTm > 0 || $watchTm > 0 || !empty($completedAt));
$completed = (!empty($completedAt));
$status = !$hasHistory ? 'not-started' : ($completed ? 'completed' : 'in-progress');
$lessonEndDate = !empty($row['end_date']) ? substr($row['end_date'], 0, 10) : null;
$chapterMap[$code]['lessons'][] = [
'content_id' => $row['content_id'],
'title' => $row['title'],
'url' => $row['content_url'],
'description' => $row['description'] ?? '',
'content_tm' => $contentTm,
'watch_tm' => $watchTm,
'all_tm' => $allTm,
'completed' => $completed,
'status' => $status,
'end_date' => $lessonEndDate,
'sort_order' => (int)($row['sort_order'] ?? 0),
];
if ($lessonEndDate !== null) {
if ($chapterMap[$code]['min_end_date'] === null || $lessonEndDate < $chapterMap[$code]['min_end_date']) {
$chapterMap[$code]['min_end_date'] = $lessonEndDate;
}
}
if (!$completed) {
$chapterMap[$code]['completed'] = false;
}
}
// 영상이 없는 챕터는 미완료 처리
foreach ($chapterMap as &$ch) {
if (empty($ch['lessons'])) {
$ch['completed'] = false;
}
}
unset($ch);
$chapters = array_values($chapterMap);
// lessons를 sort_order 기준으로 재정렬 (동률 시 content_id ASC)
foreach ($chapters as &$ch) {
usort($ch['lessons'], static function ($a, $b) {
$diff = ($a['sort_order'] ?? 0) <=> ($b['sort_order'] ?? 0);
return $diff !== 0 ? $diff : ($a['content_id'] <=> $b['content_id']);
});
}
unset($ch);
// ── 6. 전체 진도율 계산 ──────────────────────────────────────────────────
$totalLessons = 0;
$completedLessons = 0;
foreach ($chapters as $ch) {
foreach ($ch['lessons'] as $lesson) {
$totalLessons++;
if ($lesson['completed']) { $completedLessons++; }
}
}
$progressRate = $totalLessons > 0
? (int)round($completedLessons / $totalLessons * 100)
: 0;
// ── 7. D-day 계산 (edu_contents.end_date 최솟값 기준, 없으면 당해 12월 31일) ──
$minEndDate = null;
foreach ($chapters as $ch) {
if (!empty($ch['min_end_date'])) {
if ($minEndDate === null || $ch['min_end_date'] < $minEndDate) {
$minEndDate = $ch['min_end_date'];
}
}
}
$deadline = $minEndDate
? new DateTime($minEndDate)
: new DateTime(date('Y') . '-12-31');
$today = new DateTime(date('Y-m-d'));
$diff = $today->diff($deadline);
$dDay = ($today <= $deadline) ? (int)$diff->days : 0;
$deadlineStr = $deadline->format('n월 j일');
// ── 8. JS 주입용 JSON 생성 ────────────────────────────────────────────────
$chaptersJson = json_encode($chapters, JSON_UNESCAPED_UNICODE);
// window.learningConfigData 형식: { chapterId: { completed, lessons:[{completed}] } }
$configData = [];
foreach ($chapters as $ch) {
$chapterConfig = [
'completed' => $ch['completed'],
'lessons' => array_map(
fn($l) => ['completed' => $l['completed']],
$ch['lessons']
),
];
// 프론트 코드 호환을 위해 code/id 키 모두 제공
$configData[$ch['code']] = $chapterConfig;
$configData[(string)$ch['id']] = $chapterConfig;
}
$configJson = json_encode($configData, JSON_UNESCAPED_UNICODE);