Розробка системи аукціону на 1С-Бітрикс
Аукціон на сайті — це механізм продажу, де ціна товара визначається конкуренцією покупців: переможцем стає той, хто в відведений час запропонував найбільшу суму. 1С-Бітрикс не містить аукціонної логіки — її потрібно розробляти поверх стандартних модулів catalog та sale.
Типи аукціонів
Перед розробкою важливо зафіксувати бізнес-вимоги за типом аукціону:
| Тип | Опис | Особливості |
|---|---|---|
| Англійський (відкритий) | Ставки зростають, переважає максимальна | Найпоширеніший |
| Нідерландський (зворотний) | Ціна знижується, переважає перший, хто згоджується | Потрібен таймер зниження ціни |
| Авто-ставка | Вказується максимум, система торгується автоматично | Складна логіка |
| «Купити зараз» | Аукціон + фіксована ціна для миттєвої покупки | Паралельний канал продажу |
Структура даних
Аукціонний лот — розширення товарної картки. Зберігання через властивості інфоблока або окремі таблиці.
Таблиця b_local_auction:
| Поле | Тип | Опис |
|---|---|---|
| ID | int | Первинний ключ |
| PRODUCT_ID | int | Посилання на елемент інфоблока |
| START_PRICE | decimal | Стартова ціна |
| MIN_STEP | decimal | Мінімальний крок ставки |
| CURRENT_PRICE | decimal | Поточна максимальна ставка |
| CURRENT_WINNER_ID | int | ID користувача з ведучою ставкою |
| RESERVE_PRICE | decimal | Резервна ціна (приховна) |
| BUY_NOW_PRICE | decimal | Ціна «Купити зараз» (опціонально) |
| START_TIME | datetime | Початок аукціону |
| END_TIME | datetime | Кінець аукціону |
| STATUS | enum | PENDING, ACTIVE, ENDED, CANCELLED |
| BIDS_COUNT | int | Кількість ставок |
Таблиця b_local_auction_bid — історія ставок:
| Поле | Опис |
|---|---|
| AUCTION_ID | Посилання на аукціон |
| USER_ID | Учасник |
| AMOUNT | Сума ставки |
| AUTO_MAX | Максимум для авто-ставки |
| CREATED_AT | Час ставки |
| IP | IP-адреса (для антифраду) |
Логіка прийняття ставки
Прийняття нової ставки — критична секція: кілька користувачів можуть робити ставки одночасно. Без блокувань виникають race conditions.
function placeBid(int $auctionId, int $userId, float $amount): BidResult
{
// Початок транзакції з блокуванням рядка аукціону
$connection = \Bitrix\Main\Application::getConnection();
$connection->startTransaction();
try {
// SELECT FOR UPDATE — блокуємо рядок
$auction = $connection->query(
"SELECT * FROM b_local_auction WHERE ID = {$auctionId} FOR UPDATE"
)->fetch();
// Валідації
if ($auction['STATUS'] !== 'ACTIVE') {
throw new \Exception('Аукціон не активний');
}
if (new DateTime() > new DateTime($auction['END_TIME'])) {
throw new \Exception('Аукціон завершений');
}
if ($amount < $auction['CURRENT_PRICE'] + $auction['MIN_STEP']) {
throw new \Exception('Ставка нижче мінімальної: ' . ($auction['CURRENT_PRICE'] + $auction['MIN_STEP']));
}
if ($auction['CURRENT_WINNER_ID'] === $userId) {
throw new \Exception('Ви вже лідируєте');
}
// Записати ставку
AuctionBidTable::add([
'AUCTION_ID' => $auctionId,
'USER_ID' => $userId,
'AMOUNT' => $amount,
'CREATED_AT' => new DateTime(),
]);
// Оновити поточну ставку
AuctionTable::update($auctionId, [
'CURRENT_PRICE' => $amount,
'CURRENT_WINNER_ID' => $userId,
'BIDS_COUNT' => $auction['BIDS_COUNT'] + 1,
]);
// Продовження часу при ставці в останні хвилини
if ((new DateTime($auction['END_TIME']))->getTimestamp() - time() < 300) {
AuctionTable::update($auctionId, [
'END_TIME' => (new DateTime())->modify('+5 minutes'),
]);
}
$connection->commitTransaction();
// Сповістити попереднього лідера
notifyOutbid($auction['CURRENT_WINNER_ID'], $auctionId, $amount);
} catch (\Exception $e) {
$connection->rollbackTransaction();
throw $e;
}
}
Real-time оновлення
Аукціон вимагає відображення поточної ставки без перезавантаження сторінки. Варіанти:
Polling — найпростіший: JavaScript робить AJAX-запит кожні 5-10 секунд.
setInterval(() => {
fetch('/local/ajax/auction_state.php?id=' + auctionId)
.then(r => r.json())
.then(data => updateUI(data));
}, 5000);
Server-Sent Events (SSE) — сервер сам відправляє події при змінені. Менше навантаження, ніж polling.
WebSocket — максимально real-time, але вимагає окремого сервера (Node.js, Ratchet). На хостингу Бітрикс може бути недоступно.
Для більшості аукціонів polling з інтервалом 5-10 секунд — достатнє рішення.
Завершення аукціону та створення замовлення
Агент перевіряє аукціони, у яких сплив END_TIME:
$ended = AuctionTable::getList([
'filter' => ['STATUS' => 'ACTIVE', '<END_TIME' => new DateTime()]
])->fetchAll();
foreach ($ended as $auction) {
AuctionTable::update($auction['ID'], ['STATUS' => 'ENDED']);
if ($auction['CURRENT_WINNER_ID'] && $auction['CURRENT_PRICE'] >= $auction['RESERVE_PRICE']) {
// Сповістити переможця
notifyWinner($auction['CURRENT_WINNER_ID'], $auction);
// Створити замовлення або відправити посилання на оплату
createAuctionOrder($auction);
} else {
// Резервна ціна не досягнута
notifyReserveNotMet($auction);
}
}
Антифрод та правила
- Один користувач не може робити ставки від кількох аккаунтів — перевірка за IP + cookies + історія аккаунту.
- Ставки тільки від авторизованих користувачів.
- Опціонально: верифікація телефону або передоплата (блокування суми) для доступу до аукціону.
- Мінімальний аккаунт: зареєстрований N днів тому, зробив хоча б одне замовлення.
Строки розробки
| Варіант | Состав | Строк |
|---|---|---|
| Базовий аукціон | Англійський, ставки, таймер, polling | 8-10 днів |
| Розширений | Авто-ставка, «купити зараз», сповіщення | 12-16 днів |
| Повна платформа | Кілька типів, WebSocket, аналітика, антифрод | 20-30 днів |







