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
+394
View File
@@ -0,0 +1,394 @@
<?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 response_json(array $data): void
{
echo json_encode($data, JSON_UNESCAPED_UNICODE);
exit;
}
try {
$pdo = db_conn();
$pdo->exec("SET NAMES 'utf8mb4'");
$categoryBase = [
'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) {
$categoryBase[$row['base_code']] = $row['code_name'];
}
// 연도 목록
$stmtYear = $pdo->prepare("
SELECT DISTINCT YEAR(last_viewed_at) AS view_year
FROM edu_learning_histories
WHERE member_id = :member_id
AND sys_comp_code = :sys_comp_code
AND last_viewed_at IS NOT NULL
ORDER BY view_year DESC
");
$stmtYear->execute([
':member_id' => $memberId,
':sys_comp_code' => $sysCompCode,
]);
$yearList = array_map(
static fn($v) => (int)$v,
array_filter($stmtYear->fetchAll(PDO::FETCH_COLUMN))
);
$currentYear = (int)date('Y');
if (empty($yearList)) {
// 리스트가 완전히 비어있다면 현재 연도 한 개만 추가
$yearList = [$currentYear];
}
$selectedYear = isset($_GET['year']) ? (int)$_GET['year'] : 0;
if ($selectedYear <= 0) {
$selectedYear = !empty($yearList) ? (int)$yearList[0] : (int)date('Y');
}
// 내 총학습시간
$stmtMyTotal = $pdo->prepare("
SELECT COALESCE(SUM(watch_tm), 0) AS total_watch_tm
FROM edu_learning_histories
WHERE member_id = :member_id
AND sys_comp_code = :sys_comp_code
AND YEAR(last_viewed_at) = :stats_year
");
$stmtMyTotal->execute([
':member_id' => $memberId,
':sys_comp_code' => $sysCompCode,
':stats_year' => $selectedYear,
]);
$myTotalMinutes = (int)$stmtMyTotal->fetchColumn();
// 전체평균
$stmtAvg = $pdo->prepare("
SELECT COALESCE(AVG(x.user_total_watch_tm), 0) AS avg_total_watch_tm
FROM (
SELECT
member_id,
sys_comp_code,
SUM(watch_tm) AS user_total_watch_tm
FROM edu_learning_histories
WHERE YEAR(last_viewed_at) = :stats_year
GROUP BY member_id, sys_comp_code
) x
");
$stmtAvg->execute([':stats_year' => $selectedYear]);
$avgTotalMinutes = (int)round((float)$stmtAvg->fetchColumn());
// 온보딩 기준일
$stmtUser = $pdo->prepare("
SELECT join_date
FROM edu_users
WHERE member_id = :member_id
AND sys_comp_code = :sys_comp_code
LIMIT 1
");
$stmtUser->execute([
':member_id' => $memberId,
':sys_comp_code' => $sysCompCode,
]);
$joinDate = $stmtUser->fetchColumn();
$onboardingDueDate = '';
$isOnboardingPeriod = 'N';
if (!empty($joinDate)) {
$onboardingDueDate = date('Y-m-d', strtotime($joinDate . ' +14 days'));
$isOnboardingPeriod = (date('Y-m-d') <= $onboardingDueDate) ? 'Y' : 'N';
}
/**
* 현재 월을 기준으로 분기 코드 계산
* - 1~3월 : CA200Q01
* - 4~6월 : CA200Q02
* - 7~9월 : CA200Q03
* - 10~12월 : CA200Q04
*/
$currentMonth = (int)date('n');
if ($currentMonth >= 1 && $currentMonth <= 3) {
$currentQuarterBaseCode = 'CA200Q01';
} elseif ($currentMonth >= 4 && $currentMonth <= 6) {
$currentQuarterBaseCode = 'CA200Q02';
} elseif ($currentMonth >= 7 && $currentMonth <= 9) {
$currentQuarterBaseCode = 'CA200Q03';
} else {
$currentQuarterBaseCode = 'CA200Q04';
}
// --fn_get_progress_rate(:sys_comp_code, :stats_year1, :member_id, c.category_code, '') AS category_percent,
if($memberId=="test"){
}else{
// 선택연도 카테고리별 집계
$stmtCategory = $pdo->prepare("
SELECT
c.category_code,
CASE
WHEN c.category_code = 'CA10001' THEN
fn_get_progress_rate(:sys_comp_code_01, :stats_year_01, :member_id_01, c.category_code, :quarter_code_01)
ELSE
fn_get_progress_rate(:sys_comp_code_02, :stats_year_02, :member_id_02, c.category_code, '')
END AS category_percent,
COUNT(DISTINCT lh.content_id) AS learned_content_cnt,
COUNT(DISTINCT CASE WHEN lh.watch_tm >= lh.content_tm THEN lh.content_id END) AS completed_content_cnt,
COALESCE(SUM(lh.watch_tm), 0) AS total_watch_tm,
COALESCE(SUM(lh.content_tm), 0) AS total_content_tm
,FLOOR(COALESCE(SUM(lh.watch_tm), 0)/60) AS total_watch_tm_min
FROM edu_learning_histories lh
INNER JOIN edu_contents c ON c.content_id = lh.content_id
WHERE lh.member_id = :member_id_03
AND lh.sys_comp_code = :sys_comp_code_03
AND YEAR(lh.last_viewed_at) = :stats_year_03
GROUP BY c.category_code
");
$stmtCategory->execute([
':sys_comp_code_01' => $sysCompCode,
':stats_year_01' => $selectedYear,
':member_id_01' => $memberId,
':quarter_code_01' => $currentQuarterBaseCode,
':sys_comp_code_02' => $sysCompCode,
':stats_year_02' => $selectedYear,
':member_id_02' => $memberId,
':member_id_03' => $memberId,
':sys_comp_code_03' => $sysCompCode,
':stats_year_03' => $selectedYear,
]);
}
// $stmtCategory = $pdo->prepare("
// SELECT
// c.category_code,
// COUNT(DISTINCT lh.content_id) AS learned_content_cnt,
// COUNT(DISTINCT CASE WHEN lh.watch_tm >= lh.content_tm THEN lh.content_id END) AS completed_content_cnt,
// COALESCE(SUM(lh.watch_tm), 0) AS total_watch_tm,
// COALESCE(SUM(lh.content_tm), 0) AS total_content_tm
// FROM edu_learning_histories lh
// INNER JOIN edu_contents c
// ON c.content_id = lh.content_id
// WHERE lh.member_id = :member_id
// AND lh.sys_comp_code = :sys_comp_code
// AND YEAR(lh.last_viewed_at) = :stats_year
// GROUP BY c.category_code
// ");
// $stmtCategory->execute([
// ':member_id' => $memberId,
// ':sys_comp_code' => $sysCompCode,
// ':stats_year' => $selectedYear,
// ]);
$aggMap = [];
foreach ($stmtCategory->fetchAll(PDO::FETCH_ASSOC) as $row) {
$aggMap[$row['category_code']] = $row;
}
/*
// 카테고리별 전체 콘텐츠 수
$stmtTotalCnt = $pdo->prepare("
SELECT
category_code,
COUNT(*) AS total_cnt
FROM edu_contents
WHERE (is_active = '1' OR is_active = 'Y')
GROUP BY category_code
");
$stmtTotalCnt->execute();
$totalCntMap = [];
foreach ($stmtTotalCnt->fetchAll(PDO::FETCH_ASSOC) as $row) {
$totalCntMap[$row['category_code']] = (int)$row['total_cnt'];
}
*/
// ---------------------------------------------------------
// 카테고리별 전체 콘텐츠 수 산정 : Start
// - 카테고리마다 total_cnt 산정 조건이 다를 수 있으므로
// 공통 GROUP BY 방식 대신, 카테고리별 분기 구조로 처리한다.
// ---------------------------------------------------------
$totalCntMap = [
'CA10001' => 0, // 마이클래스
'CA10002' => 0, // 온보딩
'CA10003' => 0, // 법정교육
'CA10004' => 0, // 리더십
'CA10005' => 0, // 인사이트
'CA10006' => 0, // 비즈트렌드
];
// ---------------------------------------------------------
// 1. 마이클래스(CA10001)
// - 현재 분기 기준 + 선택년도 기준으로 콘텐츠 수 산정
// - 조건:
// category_code = CA10001
// category_group = 현재분기 base_code
// goal_code 앞 4자리 = 선택년도
// ---------------------------------------------------------
$stmtMyClassCnt = $pdo->prepare("
SELECT COUNT(*) AS total_cnt
FROM edu_contents
WHERE (is_active = '1' OR is_active = 'Y')
AND category_code = 'CA10001'
AND base_year = :selected_year
");
$stmtMyClassCnt->execute([
':selected_year' => (string)$selectedYear,
]);
$totalCntMap['CA10001'] = (int)$stmtMyClassCnt->fetchColumn();
// ---------------------------------------------------------
// 2. 온보딩(CA10002)
// - 현재는 기본 조건만 적용
// - 추후 카테고리별 별도 조건을 쉽게 추가할 수 있도록
// 분리된 쿼리 구조로 작성
// ---------------------------------------------------------
$stmtOnboardingCnt = $pdo->prepare("
SELECT COUNT(*) AS total_cnt
FROM edu_contents
WHERE (is_active = '1' OR is_active = 'Y')
AND category_code = 'CA10002'
");
$stmtOnboardingCnt->execute();
$totalCntMap['CA10002'] = (int)$stmtOnboardingCnt->fetchColumn();
// ---------------------------------------------------------
// 3. 법정교육(CA10003)
// - 현재는 기본 조건만 적용
// - 추후 카테고리별 별도 조건을 쉽게 추가할 수 있도록
// 분리된 쿼리 구조로 작성
// ---------------------------------------------------------
$stmtLegalCnt = $pdo->prepare("
SELECT COUNT(*) AS total_cnt
FROM edu_contents
WHERE (is_active = '1' OR is_active = 'Y')
AND category_code = 'CA10003'
AND base_year = :selected_year
");
$stmtLegalCnt->execute([
':selected_year' => (string)$selectedYear,
]);
$totalCntMap['CA10003'] = (int)$stmtLegalCnt->fetchColumn();
// ---------------------------------------------------------
// 4. 나머지 카테고리 공통 처리
// - 현재는 공통 조건으로 콘텐츠 수 산정
// - 필요 시 이후 각 카테고리도 개별 쿼리로 분리 가능
// ---------------------------------------------------------
$stmtEtcCnt = $pdo->prepare("
SELECT
category_code,
COUNT(*) AS total_cnt
FROM edu_contents
WHERE (is_active = '1' OR is_active = 'Y')
AND category_code IN ('CA10004', 'CA10005', 'CA10006')
GROUP BY category_code
");
$stmtEtcCnt->execute();
foreach ($stmtEtcCnt->fetchAll(PDO::FETCH_ASSOC) as $row) {
$totalCntMap[$row['category_code']] = (int)$row['total_cnt'];
}
// ---------------------------------------------------------
// 카테고리별 전체 콘텐츠 수 산정 : End
// ---------------------------------------------------------
$activityItems = [];
foreach ($categoryBase as $categoryCode => $categoryName) {
$agg = $aggMap[$categoryCode] ?? null;
$learnedContentCnt = (int)($agg['learned_content_cnt'] ?? 0);
$completedContentCnt = (int)($agg['completed_content_cnt'] ?? 0);
$totalWatchTm = (int)($agg['total_watch_tm'] ?? 0);
$percent = (int)($agg['category_percent'] ?? 0);
$totalContentTm = (int)($agg['total_content_tm'] ?? 0);
$masterTotalCnt = (int)($totalCntMap[$categoryCode] ?? 0);
// 1차안: gauge는 학습이력 기준 content_tm 합계가 있을 때 그 기준으로 계산
// schema상 edu_contents에 표준 duration 컬럼이 없어 완전한 "전체 기준 분모"는 별도 운영정책 확정 필요
//$percent = ($totalContentTm > 0) ? (int)round(($totalWatchTm / $totalContentTm) * 100) : 0;
//$percent = max(0, min(100, $percent));
$activityItems[] = [
'category_code' => $categoryCode,
'category_name' => $categoryName,
'learned_content_cnt' => $learnedContentCnt,
'completed_content_cnt' => $completedContentCnt,
'master_total_cnt' => $masterTotalCnt,
'total_watch_tm' => $totalWatchTm,
'total_content_tm' => $totalContentTm,
'percent' => $percent,
'onboarding_due_date' => ($categoryCode === 'CA10002') ? $onboardingDueDate : '',
'is_onboarding_period' => ($categoryCode === 'CA10002') ? $isOnboardingPeriod : 'N',
];
}
response_json([
'success' => true,
'year_list' => $yearList,
'selected_year' => $selectedYear,
'my_total_minutes' => $myTotalMinutes,
'avg_total_minutes' => $avgTotalMinutes,
'onboarding_due_date'=> $onboardingDueDate,
'is_onboarding_period' => $isOnboardingPeriod,
'activity_items' => $activityItems,
]);
} catch (Throwable $e) {
response_json([
'success' => false,
'message' => '학습활동 데이터를 불러오지 못했습니다.',
'error' => $e->getMessage(),
]);
}