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
+268
View File
@@ -0,0 +1,268 @@
<?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';
function json_exit(array $payload, int $status = 200): void {
http_response_code($status);
echo json_encode($payload, JSON_UNESCAPED_UNICODE);
exit;
}
function resolve_member_and_company(PDO $pdo): array {
$sessionMemberId = trim((string)($_SESSION['ss_mb_id'] ?? $_SESSION['member_id'] ?? ''));
$memberId = $sessionMemberId !== '' ? $sessionMemberId : 'U001';
$stmtUser = $pdo->prepare('SELECT sys_comp_code FROM edu_users WHERE member_id = ? ORDER BY sys_comp_code LIMIT 1');
$stmtUser->execute([$memberId]);
$sysCompCode = (string)($stmtUser->fetchColumn() ?: '');
if ($sysCompCode === '') {
json_exit([
'success' => false,
'message' => 'edu_users에 회원 정보가 없어 마이클래스 상태를 조회할 수 없습니다.',
'data' => ['member_id' => $memberId],
], 400);
}
return [$memberId, $sysCompCode];
}
function ensure_goal_contents(PDO $pdo, string $goalCode, string $quarter, string $actor): int {
$stmtCnt = $pdo->prepare("SELECT COUNT(*) FROM edu_goal_contents WHERE goal_code = ? AND (is_active = '1' OR is_active IS NULL)");
$stmtCnt->execute([$goalCode]);
$existing = (int)$stmtCnt->fetchColumn();
if ($existing >= 6) {
return $existing;
}
$stmtCandidates = $pdo->prepare(
"SELECT c.content_id
FROM edu_contents c
WHERE (c.is_active = '1' OR c.is_active IS NULL)
AND (c.goal_code = :goal_code OR c.category_group = :quarter)
ORDER BY
CASE WHEN c.sort_order IS NULL THEN 1 ELSE 0 END,
c.sort_order ASC,
c.content_id ASC
LIMIT 6"
);
$stmtCandidates->execute([
':goal_code' => $goalCode,
':quarter' => $quarter,
]);
$candidateIds = $stmtCandidates->fetchAll(PDO::FETCH_COLUMN);
if (count($candidateIds) < 6) {
$stmtFallback = $pdo->prepare(
"SELECT c.content_id
FROM edu_contents c
WHERE (c.is_active = '1' OR c.is_active IS NULL)
ORDER BY
CASE WHEN c.sort_order IS NULL THEN 1 ELSE 0 END,
c.sort_order ASC,
c.content_id ASC
LIMIT 6"
);
$stmtFallback->execute();
$candidateIds = $stmtFallback->fetchAll(PDO::FETCH_COLUMN);
}
$candidateIds = array_values(array_unique(array_filter(array_map('strval', $candidateIds))));
$stmtUpsert = $pdo->prepare(
"INSERT INTO edu_goal_contents
(goal_code, content_id, index_id, is_active, sort_order, created_by, created_at, updated_by, updated_at)
VALUES
(?, ?, ?, '1', ?, ?, NOW(), ?, NOW())
ON DUPLICATE KEY UPDATE
is_active = VALUES(is_active),
sort_order = VALUES(sort_order),
updated_by = VALUES(updated_by),
updated_at = NOW()"
);
foreach ($candidateIds as $idx => $contentId) {
$order = $idx + 1;
$stmtUpsert->execute([$goalCode, $contentId, $order, $order, $actor, $actor]);
}
$stmtCnt->execute([$goalCode]);
return (int)$stmtCnt->fetchColumn();
}
try {
$pdo = db_conn();
[$memberId, $sysCompCode] = resolve_member_and_company($pdo);
$stmtGoals = $pdo->query(
"SELECT goal_code, title, quarter, goal_no, sort_order
FROM edu_learning_goals
WHERE (is_active = '1' OR is_active IS NULL)
ORDER BY base_year DESC, quarter ASC, goal_no ASC, goal_code ASC"
);
$goals = $stmtGoals->fetchAll(PDO::FETCH_ASSOC);
if (!$goals) {
json_exit(['success' => true, 'data' => ['goals' => [], 'videos' => []]]);
}
$stmtSelected = $pdo->prepare(
"SELECT ug.goal_code, ug.quarter, ug.completed_date, ug.updated_at, lg.title
FROM edu_user_learning_goals ug
LEFT JOIN edu_learning_goals lg ON lg.goal_code = ug.goal_code
WHERE ug.member_id = ?
AND ug.sys_comp_code = ?
AND (ug.is_active = '1' OR ug.is_active IS NULL)
ORDER BY ug.updated_at DESC, ug.goal_code DESC"
);
$stmtSelected->execute([$memberId, $sysCompCode]);
$selectedGoals = $stmtSelected->fetchAll(PDO::FETCH_ASSOC);
$requestedGoalCode = trim((string)($_GET['goal_code'] ?? ''));
$activeGoalCode = $requestedGoalCode;
if ($activeGoalCode === '' && !empty($selectedGoals)) {
$activeGoalCode = (string)$selectedGoals[0]['goal_code'];
}
if ($activeGoalCode === '') {
$activeGoalCode = (string)$goals[0]['goal_code'];
}
$goalIndex = [];
foreach ($goals as $g) {
$goalIndex[(string)$g['goal_code']] = $g;
}
if (!isset($goalIndex[$activeGoalCode])) {
$activeGoalCode = (string)$goals[0]['goal_code'];
}
$activeQuarter = (string)($goalIndex[$activeGoalCode]['quarter'] ?? '');
ensure_goal_contents($pdo, $activeGoalCode, $activeQuarter, $memberId);
$stmtVideos = $pdo->prepare(
"SELECT
gc.goal_code,
gc.content_id,
gc.sort_order,
gc.index_id,
c.title,
c.description,
c.description2,
c.content_url,
c.thumbnail_url,
c.category_code,
c.category_group,
lh.watch_tm,
lh.content_tm,
lh.completed_at
FROM edu_goal_contents gc
JOIN edu_contents c ON c.content_id = gc.content_id
LEFT JOIN edu_learning_histories lh
ON lh.member_id = ?
AND lh.sys_comp_code = ?
AND lh.content_id = gc.content_id
WHERE gc.goal_code = ?
AND (gc.is_active = '1' OR gc.is_active IS NULL)
ORDER BY
CASE WHEN gc.sort_order IS NULL THEN 1 ELSE 0 END,
gc.sort_order ASC,
CASE WHEN gc.index_id IS NULL THEN 1 ELSE 0 END,
gc.index_id ASC,
gc.content_id ASC
LIMIT 6"
);
$stmtVideos->execute([$memberId, $sysCompCode, $activeGoalCode]);
$videos = $stmtVideos->fetchAll(PDO::FETCH_ASSOC);
$contentIds = array_values(array_unique(array_filter(array_map(static function ($r) {
return (string)($r['content_id'] ?? '');
}, $videos))));
$memoMap = [];
if (!empty($contentIds)) {
$in = implode(',', array_fill(0, count($contentIds), '?'));
$stmtMemo = $pdo->prepare(
"SELECT content_id, seq, title
FROM edu_content_memos
WHERE content_id IN ($in)
AND (is_active = '1' OR is_active IS NULL)
ORDER BY content_id ASC, seq ASC"
);
$stmtMemo->execute($contentIds);
$memoRows = $stmtMemo->fetchAll(PDO::FETCH_ASSOC);
foreach ($memoRows as $m) {
$cid = (string)$m['content_id'];
if (!isset($memoMap[$cid])) {
$memoMap[$cid] = [];
}
$memoTitle = trim((string)($m['title'] ?? ''));
if ($memoTitle !== '') {
$memoMap[$cid][] = $memoTitle;
}
}
}
foreach ($videos as $idx => $video) {
$cid = (string)$video['content_id'];
$videos[$idx]['memos'] = $memoMap[$cid] ?? [];
$videos[$idx]['is_completed'] = !empty($video['completed_at']);
$videos[$idx]['slot_no'] = $idx + 1;
}
$stmtProgress = $pdo->prepare(
"SELECT
ug.goal_code,
COUNT(gc.content_id) AS total_count,
SUM(CASE WHEN lh.completed_at IS NOT NULL THEN 1 ELSE 0 END) AS completed_count
FROM edu_user_learning_goals ug
LEFT JOIN edu_goal_contents gc
ON gc.goal_code = ug.goal_code
AND (gc.is_active = '1' OR gc.is_active IS NULL)
LEFT JOIN edu_learning_histories lh
ON lh.member_id = ug.member_id
AND lh.sys_comp_code = ug.sys_comp_code
AND lh.content_id = gc.content_id
WHERE ug.member_id = ?
AND ug.sys_comp_code = ?
AND (ug.is_active = '1' OR ug.is_active IS NULL)
GROUP BY ug.goal_code"
);
$stmtProgress->execute([$memberId, $sysCompCode]);
$progressRows = $stmtProgress->fetchAll(PDO::FETCH_ASSOC);
$goalProgress = [];
foreach ($progressRows as $row) {
$total = (int)($row['total_count'] ?? 0);
$completed = (int)($row['completed_count'] ?? 0);
$goalProgress[(string)$row['goal_code']] = [
'total_count' => $total,
'completed_count' => $completed,
'is_completed' => $total > 0 && $completed >= $total,
];
}
json_exit([
'success' => true,
'data' => [
'member_id' => $memberId,
'sys_comp_code' => $sysCompCode,
'active_goal_code' => $activeGoalCode,
'goals' => $goals,
'selected_goals' => $selectedGoals,
'goal_progress' => $goalProgress,
'videos' => $videos,
],
]);
} catch (Throwable $e) {
json_exit([
'success' => false,
'message' => $e->getMessage(),
], 500);
}