243 lines
8.8 KiB
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);
|