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'], ]);