Initial commit: 교육 프로젝트 배포
This commit is contained in:
@@ -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'],
|
||||
]);
|
||||
Reference in New Issue
Block a user