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
+180
View File
@@ -0,0 +1,180 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/common.php';
require_once dirname(__DIR__) . '/db_conn.php';
api_header_json();
$user = api_require_login();
$method = strtoupper((string)($_SERVER['REQUEST_METHOD'] ?? 'GET'));
/**
* Normalize keyword for storage (trim + max 50 chars).
*/
function normalize_keyword(string $value): string {
$keyword = trim($value);
if ($keyword === '') {
return '';
}
if (function_exists('mb_substr')) {
return mb_substr($keyword, 0, 50, 'UTF-8');
}
return substr($keyword, 0, 50);
}
/**
* Insert one search log row using per-user seq and short retry on PK conflicts.
*/
function insert_search_log(PDO $pdo, string $memberId, string $sysCompCode, string $keyword): array {
$maxAttempts = 3;
for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
try {
$pdo->beginTransaction();
$stmtSeq = $pdo->prepare(
'SELECT COALESCE(MAX(seq), 0) + 1 AS next_seq
FROM edu_search_logs
WHERE member_id = ?
AND sys_comp_code = ?'
);
$stmtSeq->execute([$memberId, $sysCompCode]);
$nextSeq = (int)($stmtSeq->fetchColumn() ?: 1);
$stmtInsert = $pdo->prepare(
'INSERT INTO edu_search_logs
(member_id, sys_comp_code, seq, keyword, searched_at)
VALUES
(?, ?, ?, ?, NOW())'
);
$stmtInsert->execute([$memberId, $sysCompCode, $nextSeq, $keyword]);
$pdo->commit();
return [
'seq' => $nextSeq,
'keyword' => $keyword,
'searched_at' => date('Y-m-d H:i:s'),
'retry_count' => $attempt - 1,
];
} catch (Throwable $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
$sqlState = '';
if ($e instanceof PDOException && isset($e->errorInfo[0])) {
$sqlState = (string)$e->errorInfo[0];
}
$isDuplicateKey = ($sqlState === '23000');
if (!$isDuplicateKey || $attempt >= $maxAttempts) {
throw $e;
}
}
}
throw new RuntimeException('search_log_insert_retry_exceeded');
}
if ($method === 'GET') {
$windowDays = 7;
$limit = (int)($_GET['limit'] ?? 20);
if ($limit < 1) {
$limit = 1;
} elseif ($limit > 50) {
$limit = 50;
}
try {
$pdo = db_conn();
$stmt = $pdo->prepare(
'SELECT seq, keyword, searched_at
FROM edu_search_logs
WHERE member_id = ?
AND sys_comp_code = ?
AND searched_at >= (NOW() - INTERVAL 7 DAY)
ORDER BY searched_at DESC, seq DESC
LIMIT ' . $limit
);
$stmt->execute([(string)$user['member_id'], (string)$user['sys_comp_code']]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
$logs = array_map(static function (array $row): array {
return [
'seq' => (int)($row['seq'] ?? 0),
'keyword' => (string)($row['keyword'] ?? ''),
'searched_at' => (string)($row['searched_at'] ?? ''),
];
}, $rows);
echo json_encode([
'success' => true,
'data' => [
'logs' => $logs,
'window_days' => $windowDays,
'limit' => $limit,
'member_id' => (string)$user['member_id'],
'sys_comp_code' => (string)$user['sys_comp_code'],
],
'meta' => [
'api' => 'search_logs',
'version' => 1,
'status' => 'ok',
],
], JSON_UNESCAPED_UNICODE);
exit;
} catch (Throwable $e) {
error_log('[search_logs][GET] ' . $e->getMessage());
api_error(500, 'search_logs_fetch_failed');
}
}
if ($method === 'POST') {
$input = api_get_input();
$keyword = normalize_keyword((string)($input['keyword'] ?? ''));
if ($keyword === '') {
api_error(400, 'keyword_required');
}
try {
$pdo = db_conn();
$inserted = insert_search_log(
$pdo,
(string)$user['member_id'],
(string)$user['sys_comp_code'],
$keyword
);
echo json_encode([
'success' => true,
'data' => [
'accepted' => true,
'keyword' => $inserted['keyword'],
'seq' => (int)$inserted['seq'],
'searched_at' => (string)$inserted['searched_at'],
'member_id' => (string)$user['member_id'],
'sys_comp_code' => (string)$user['sys_comp_code'],
],
'meta' => [
'api' => 'search_logs',
'version' => 1,
'status' => 'ok',
'retry_count' => (int)$inserted['retry_count'],
],
], JSON_UNESCAPED_UNICODE);
exit;
} catch (Throwable $e) {
error_log('[search_logs][POST] ' . $e->getMessage());
api_error(500, 'search_log_insert_failed');
}
}
api_error(405, 'method_not_allowed', [
'allowed_methods' => ['GET', 'POST'],
]);