feat: GTSIT 사이트 구축
This commit is contained in:
111
app/Repositories/InquiryRepository.php
Normal file
111
app/Repositories/InquiryRepository.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
use App\Database;
|
||||
use PDO;
|
||||
|
||||
final class InquiryRepository
|
||||
{
|
||||
private PDO $db;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->db = Database::connection();
|
||||
}
|
||||
|
||||
public function create(array $data): string
|
||||
{
|
||||
do {
|
||||
$ticket = 'GTS-' . date('Ymd') . '-' . strtoupper(bin2hex(random_bytes(3)));
|
||||
$check = $this->db->prepare('SELECT COUNT(*) FROM inquiries WHERE ticket = :ticket');
|
||||
$check->execute(['ticket' => $ticket]);
|
||||
} while ((int) $check->fetchColumn() > 0);
|
||||
|
||||
$statement = $this->db->prepare(
|
||||
"INSERT INTO inquiries
|
||||
(ticket, name, phone, email, inquiry_type, title, body, password_hash, status, consent_at)
|
||||
VALUES (:ticket, :name, :phone, :email, :inquiry_type, :title, :body, :password_hash, 'pending', NOW())"
|
||||
);
|
||||
$statement->execute([
|
||||
'ticket' => $ticket,
|
||||
'name' => $data['name'],
|
||||
'phone' => $data['phone'],
|
||||
'email' => $data['email'] ?: null,
|
||||
'inquiry_type' => $data['type'],
|
||||
'title' => $data['title'],
|
||||
'body' => $data['body'],
|
||||
'password_hash' => password_hash($data['password'], PASSWORD_DEFAULT),
|
||||
]);
|
||||
|
||||
return $ticket;
|
||||
}
|
||||
|
||||
public function findForLookup(string $ticket): ?array
|
||||
{
|
||||
$statement = $this->db->prepare(
|
||||
'SELECT id, ticket, inquiry_type, title, body, status, reply, replied_at, created_at,
|
||||
password_hash, lookup_failed_attempts, lookup_locked_until
|
||||
FROM inquiries WHERE ticket = :ticket AND deleted_at IS NULL LIMIT 1'
|
||||
);
|
||||
$statement->execute(['ticket' => strtoupper($ticket)]);
|
||||
$inquiry = $statement->fetch();
|
||||
return $inquiry ?: null;
|
||||
}
|
||||
|
||||
public function resetLookupLimit(int $id): void
|
||||
{
|
||||
$statement = $this->db->prepare(
|
||||
'UPDATE inquiries
|
||||
SET lookup_failed_attempts = 0, lookup_locked_until = NULL
|
||||
WHERE id = :id AND deleted_at IS NULL'
|
||||
);
|
||||
$statement->execute(['id' => $id]);
|
||||
}
|
||||
|
||||
public function recordLookupFailure(int $id): bool
|
||||
{
|
||||
$this->db->beginTransaction();
|
||||
try {
|
||||
$select = $this->db->prepare(
|
||||
'SELECT lookup_failed_attempts, lookup_locked_until
|
||||
FROM inquiries WHERE id = :id AND deleted_at IS NULL FOR UPDATE'
|
||||
);
|
||||
$select->execute(['id' => $id]);
|
||||
$limit = $select->fetch();
|
||||
if (!$limit) {
|
||||
$this->db->rollBack();
|
||||
return false;
|
||||
}
|
||||
|
||||
$attempts = (int) $limit['lookup_failed_attempts'];
|
||||
$lockedUntil = $limit['lookup_locked_until'];
|
||||
if (is_string($lockedUntil) && strtotime($lockedUntil) <= time()) {
|
||||
$attempts = 0;
|
||||
}
|
||||
$attempts++;
|
||||
$locked = $attempts >= 5;
|
||||
|
||||
$update = $this->db->prepare(
|
||||
'UPDATE inquiries
|
||||
SET lookup_failed_attempts = :attempts,
|
||||
lookup_locked_until = CASE WHEN :locked = 1 THEN DATE_ADD(NOW(), INTERVAL 15 MINUTE) ELSE NULL END
|
||||
WHERE id = :id'
|
||||
);
|
||||
$update->execute([
|
||||
'attempts' => $attempts,
|
||||
'locked' => $locked ? 1 : 0,
|
||||
'id' => $id,
|
||||
]);
|
||||
$this->db->commit();
|
||||
return $locked;
|
||||
} catch (\Throwable $error) {
|
||||
if ($this->db->inTransaction()) {
|
||||
$this->db->rollBack();
|
||||
}
|
||||
throw $error;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user