Реализация Real-Time аукциона на сайте

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация Real-Time аукциона на сайте
Средняя
~5 рабочих дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Реализация Real-Time аукциона на сайте

Онлайн-аукцион требует точной синхронизации ставок в реальном времени: отображение актуальной цены у всех участников, таймер, сигналы о новых ставках, определение победителя при окончании.

Серверная логика

class AuctionService {
  async placeBid(auctionId: string, userId: string, amount: number): Promise<BidResult> {
    // Пессимистичная блокировка через Redis SETNX
    const lockKey = `lock:auction:${auctionId}`;
    const lockAcquired = await redis.set(lockKey, userId, 'NX', 'PX', 5000);

    if (!lockAcquired) {
      throw new Error('Auction is processing another bid, try again');
    }

    try {
      const auction = await auctionRepo.findById(auctionId);

      // Валидация
      if (auction.status !== 'active') throw new BidError('Auction is not active');
      if (new Date() > auction.endsAt) throw new BidError('Auction has ended');
      if (amount <= auction.currentPrice) {
        throw new BidError(`Bid must be higher than ${auction.currentPrice}`);
      }
      if (amount < auction.currentPrice + auction.minIncrement) {
        throw new BidError(`Minimum increment is ${auction.minIncrement}`);
      }
      if (auction.currentLeaderId === userId) {
        throw new BidError('You are already the highest bidder');
      }

      // Сохранить ставку
      const bid = await bidRepo.create({ auctionId, userId, amount });
      await auctionRepo.updateCurrentPrice(auctionId, amount, userId);

      // Anti-sniping: если ставка сделана в последние 2 минуты — продлить на 2 минуты
      const timeLeft = auction.endsAt.getTime() - Date.now();
      if (timeLeft < 2 * 60 * 1000) {
        const newEndTime = new Date(Date.now() + 2 * 60 * 1000);
        await auctionRepo.extendTime(auctionId, newEndTime);
      }

      // Уведомить всех участников
      await this.broadcastBid(auctionId, bid, auction);

      return { success: true, bid };

    } finally {
      await redis.del(lockKey);
    }
  }

  async broadcastBid(auctionId: string, bid: Bid, auction: Auction) {
    io.to(`auction:${auctionId}`).emit('bid:new', {
      bidId: bid.id,
      amount: bid.amount,
      bidderId: bid.userId,
      bidderName: anonymizeBidder(bid.userId),
      timestamp: bid.createdAt,
      totalBids: auction.bidCount + 1,
      newEndTime: auction.endsAt
    });
  }
}

Таймер на сервере

class AuctionTimer {
  async startTimer(auctionId: string, endTime: Date): Promise<void> {
    const msUntilEnd = endTime.getTime() - Date.now();

    // Отложенная задача — завершить аукцион точно в срок
    setTimeout(async () => {
      await this.finalizeAuction(auctionId);
    }, msUntilEnd);

    // Broadcast каждую секунду последние 60 секунд
    const broadcastInterval = setInterval(async () => {
      const remaining = endTime.getTime() - Date.now();

      if (remaining <= 0) {
        clearInterval(broadcastInterval);
        return;
      }

      if (remaining <= 60000) {
        io.to(`auction:${auctionId}`).emit('timer:tick', {
          remaining: Math.ceil(remaining / 1000)
        });
      }
    }, 1000);
  }

  async finalizeAuction(auctionId: string): Promise<void> {
    const auction = await auctionRepo.findById(auctionId);
    if (auction.status !== 'active') return;  // уже завершён

    await auctionRepo.finalize(auctionId);

    io.to(`auction:${auctionId}`).emit('auction:ended', {
      winnerId: auction.currentLeaderId,
      winnerName: await getUserName(auction.currentLeaderId),
      finalPrice: auction.currentPrice
    });

    // Уведомить победителя и предыдущих участников
    await this.notifyParticipants(auction);
  }
}

Клиентский компонент

function AuctionRoom({ auctionId }) {
  const [auction, setAuction] = useState<AuctionState>();
  const [bids, setBids] = useState<Bid[]>([]);
  const [timeLeft, setTimeLeft] = useState<number>(0);
  const socket = useSocket();

  useEffect(() => {
    if (!socket) return;

    socket.emit('auction:join', { auctionId });

    socket.on('auction:state', (state) => setAuction(state));

    socket.on('bid:new', (bid) => {
      setBids(prev => [bid, ...prev].slice(0, 50));
      setAuction(prev => prev ? { ...prev, currentPrice: bid.amount } : prev);
    });

    socket.on('timer:tick', ({ remaining }) => setTimeLeft(remaining));

    socket.on('auction:ended', ({ winnerId, finalPrice }) => {
      setAuction(prev => prev ? { ...prev, status: 'ended' } : prev);
      if (winnerId === currentUserId) {
        showCongratulations(finalPrice);
      }
    });

    return () => socket.emit('auction:leave', { auctionId });
  }, [socket, auctionId]);

  const placeBid = async (amount: number) => {
    try {
      await fetch(`/api/auctions/${auctionId}/bids`, {
        method: 'POST',
        body: JSON.stringify({ amount })
      });
    } catch (e) {
      showError(e.message);
    }
  };

  return (
    <div className="auction-room">
      <CurrentPrice price={auction?.currentPrice} />
      <AuctionTimer seconds={timeLeft} critical={timeLeft < 30} />
      <BidForm
        minBid={(auction?.currentPrice ?? 0) + (auction?.minIncrement ?? 100)}
        onBid={placeBid}
        disabled={auction?.status !== 'active'}
      />
      <BidHistory bids={bids} currentUserId={currentUserId} />
    </div>
  );
}

Сроки реализации

  • Базовый аукцион с ставками и таймером — 2–3 недели
  • Anti-sniping, уведомления, история ставок — ещё 1 неделя
  • Аукцион с несколькими лотами и оплатой — 4–6 недель