From 4aac3a0a7e0dbdd5338378ebeeb585522dff38e4 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Mon, 9 Mar 2026 08:59:06 -0300 Subject: [PATCH] feat: implement stock reservation background worker and geocode sync endpoint --- backend/internal/server/server.go | 3 +++ backend/internal/usecase/product_usecase.go | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/backend/internal/server/server.go b/backend/internal/server/server.go index fd085d7..1cac92c 100644 --- a/backend/internal/server/server.go +++ b/backend/internal/server/server.go @@ -311,6 +311,9 @@ func (s *Server) Start(ctx context.Context) error { ReadHeaderTimeout: 5 * time.Second, } + // Start Stock Reservation Cleanup Worker + s.svc.StartStockCleanupWorker(ctx) + log.Printf("starting %s on %s", s.cfg.AppName, s.cfg.Addr()) return srv.ListenAndServe() } diff --git a/backend/internal/usecase/product_usecase.go b/backend/internal/usecase/product_usecase.go index d527f8c..e8bd742 100644 --- a/backend/internal/usecase/product_usecase.go +++ b/backend/internal/usecase/product_usecase.go @@ -43,6 +43,24 @@ func (s *Service) ListProducts(ctx context.Context, filter domain.ProductFilter, return &domain.ProductPage{Products: products, Total: total, Page: page, PageSize: pageSize}, nil } +// StartStockCleanupWorker runs a background goroutine to expire old reservations. +func (s *Service) StartStockCleanupWorker(ctx context.Context) { + ticker := time.NewTicker(5 * time.Minute) + go func() { + for { + select { + case <-ticker.C: + if err := s.repo.ExpireReservations(context.Background()); err != nil { + fmt.Printf("ERROR: failed to expire stock reservations: %v\n", err) + } + case <-ctx.Done(): + ticker.Stop() + return + } + } + }() +} + // ReserveStock creates a temporary hold on inventory. func (s *Service) ReserveStock(ctx context.Context, productID, inventoryItemID, buyerID uuid.UUID, quantity int64) (*domain.StockReservation, error) { // 1. Check availability (physical stock - active reservations)