From fa9ddbdeefcd89a951437551a3be76de718ace37 Mon Sep 17 00:00:00 2001 From: Tiago Ribeiro Date: Mon, 9 Mar 2026 09:23:32 -0300 Subject: [PATCH] fix: restore missing interface methods and cleanup syntax in usecase.go and postgres.go --- .../internal/repository/postgres/postgres.go | 6 +- backend/internal/usecase/usecase.go | 253 +++++++++--------- 2 files changed, 133 insertions(+), 126 deletions(-) diff --git a/backend/internal/repository/postgres/postgres.go b/backend/internal/repository/postgres/postgres.go index 59da4a3..fba0b27 100644 --- a/backend/internal/repository/postgres/postgres.go +++ b/backend/internal/repository/postgres/postgres.go @@ -1607,6 +1607,8 @@ func (r *Repository) CreateAddress(ctx context.Context, address *domain.Address) _, err := r.db.NamedExecContext(ctx, query, address) return err +} + func (r *Repository) ListAddresses(ctx context.Context, entityID uuid.UUID) ([]domain.Address, error) { var addresses []domain.Address query := `SELECT id, entity_id, title, zip_code, street, number, complement, district, city, state, latitude, longitude, created_at, updated_at FROM addresses WHERE entity_id = $1 ORDER BY created_at DESC` @@ -1620,10 +1622,6 @@ func (r *Repository) ListAllAddresses(ctx context.Context) ([]domain.Address, er err := r.db.SelectContext(ctx, &addresses, query) return addresses, err } - // If no rows, SelectContext returns nil error but empty slice if initialized, or maybe error? - // sqlx returns error if slice is empty? No, SelectContext handles empty result by returning empty slice usually. - return addresses, err -} func (r *Repository) GetAddress(ctx context.Context, id uuid.UUID) (*domain.Address, error) { var addr domain.Address diff --git a/backend/internal/usecase/usecase.go b/backend/internal/usecase/usecase.go index fc613f9..3339f89 100644 --- a/backend/internal/usecase/usecase.go +++ b/backend/internal/usecase/usecase.go @@ -1,107 +1,107 @@ package usecase import ( -"context" -"time" + "context" + "time" -"github.com/gofrs/uuid/v5" + "github.com/gofrs/uuid/v5" -"github.com/saveinmed/backend-go/internal/domain" -"github.com/saveinmed/backend-go/internal/infrastructure/mapbox" -"github.com/saveinmed/backend-go/internal/infrastructure/notifications" + "github.com/saveinmed/backend-go/internal/domain" + "github.com/saveinmed/backend-go/internal/infrastructure/mapbox" + "github.com/saveinmed/backend-go/internal/infrastructure/notifications" ) // Repository defines the persistence contract for all core entities. -// Implementations live in internal/repository/postgres/. type Repository interface { -CreateCompany(ctx context.Context, company *domain.Company) error -ListCompanies(ctx context.Context, filter domain.CompanyFilter) ([]domain.Company, int64, error) -GetCompany(ctx context.Context, id uuid.UUID) (*domain.Company, error) -UpdateCompany(ctx context.Context, company *domain.Company) error -DeleteCompany(ctx context.Context, id uuid.UUID) error + CreateCompany(ctx context.Context, company *domain.Company) error + ListCompanies(ctx context.Context, filter domain.CompanyFilter) ([]domain.Company, int64, error) + GetCompany(ctx context.Context, id uuid.UUID) (*domain.Company, error) + UpdateCompany(ctx context.Context, company *domain.Company) error + DeleteCompany(ctx context.Context, id uuid.UUID) error -CreateProduct(ctx context.Context, product *domain.Product) error -BatchCreateProducts(ctx context.Context, products []domain.Product) error -ListProducts(ctx context.Context, filter domain.ProductFilter) ([]domain.Product, int64, error) -SearchProducts(ctx context.Context, filter domain.ProductSearchFilter) ([]domain.ProductWithDistance, int64, error) -ListRecords(ctx context.Context, filter domain.RecordSearchFilter) ([]domain.Product, int64, error) -GetProduct(ctx context.Context, id uuid.UUID) (*domain.Product, error) -UpdateProduct(ctx context.Context, product *domain.Product) error -DeleteProduct(ctx context.Context, id uuid.UUID) error -AdjustInventory(ctx context.Context, productID uuid.UUID, delta int64, reason string) (*domain.InventoryItem, error) -ListInventory(ctx context.Context, filter domain.InventoryFilter) ([]domain.InventoryItem, int64, error) -CreateInventoryItem(ctx context.Context, item *domain.InventoryItem) error -GetInventoryItem(ctx context.Context, id uuid.UUID) (*domain.InventoryItem, error) -UpdateInventoryItem(ctx context.Context, item *domain.InventoryItem) error + CreateProduct(ctx context.Context, product *domain.Product) error + BatchCreateProducts(ctx context.Context, products []domain.Product) error + ListProducts(ctx context.Context, filter domain.ProductFilter) ([]domain.Product, int64, error) + SearchProducts(ctx context.Context, filter domain.ProductSearchFilter) ([]domain.ProductWithDistance, int64, error) + ListRecords(ctx context.Context, filter domain.RecordSearchFilter) ([]domain.Product, int64, error) + GetProduct(ctx context.Context, id uuid.UUID) (*domain.Product, error) + UpdateProduct(ctx context.Context, product *domain.Product) error + DeleteProduct(ctx context.Context, id uuid.UUID) error + AdjustInventory(ctx context.Context, productID uuid.UUID, delta int64, reason string) (*domain.InventoryItem, error) + ListInventory(ctx context.Context, filter domain.InventoryFilter) ([]domain.InventoryItem, int64, error) + CreateInventoryItem(ctx context.Context, item *domain.InventoryItem) error + GetInventoryItem(ctx context.Context, id uuid.UUID) (*domain.InventoryItem, error) + UpdateInventoryItem(ctx context.Context, item *domain.InventoryItem) error -CreateOrder(ctx context.Context, order *domain.Order) error -ListOrders(ctx context.Context, filter domain.OrderFilter) ([]domain.Order, int64, error) -GetOrder(ctx context.Context, id uuid.UUID) (*domain.Order, error) -UpdateOrderStatus(ctx context.Context, id uuid.UUID, status domain.OrderStatus) error -DeleteOrder(ctx context.Context, id uuid.UUID) error -UpdateOrderItems(ctx context.Context, orderID uuid.UUID, items []domain.OrderItem, totalCents int64) error -CreateShipment(ctx context.Context, shipment *domain.Shipment) error -GetShipmentByOrderID(ctx context.Context, orderID uuid.UUID) (*domain.Shipment, error) + CreateOrder(ctx context.Context, order *domain.Order) error + ListOrders(ctx context.Context, filter domain.OrderFilter) ([]domain.Order, int64, error) + GetOrder(ctx context.Context, id uuid.UUID) (*domain.Order, error) + UpdateOrderStatus(ctx context.Context, id uuid.UUID, status domain.OrderStatus) error + DeleteOrder(ctx context.Context, id uuid.UUID) error + UpdateOrderItems(ctx context.Context, orderID uuid.UUID, items []domain.OrderItem, totalCents int64) error + CreateShipment(ctx context.Context, shipment *domain.Shipment) error + GetShipmentByOrderID(ctx context.Context, orderID uuid.UUID) (*domain.Shipment, error) -CreateUser(ctx context.Context, user *domain.User) error -ListUsers(ctx context.Context, filter domain.UserFilter) ([]domain.User, int64, error) -GetUser(ctx context.Context, id uuid.UUID) (*domain.User, error) -GetUserByUsername(ctx context.Context, username string) (*domain.User, error) -GetUserByEmail(ctx context.Context, email string) (*domain.User, error) -UpdateUser(ctx context.Context, user *domain.User) error -DeleteUser(ctx context.Context, id uuid.UUID) error + CreateUser(ctx context.Context, user *domain.User) error + ListUsers(ctx context.Context, filter domain.UserFilter) ([]domain.User, int64, error) + GetUser(ctx context.Context, id uuid.UUID) (*domain.User, error) + GetUserByUsername(ctx context.Context, username string) (*domain.User, error) + GetUserByEmail(ctx context.Context, email string) (*domain.User, error) + UpdateUser(ctx context.Context, user *domain.User) error + DeleteUser(ctx context.Context, id uuid.UUID) error -AddCartItem(ctx context.Context, item *domain.CartItem) (*domain.CartItem, error) -ListCartItems(ctx context.Context, buyerID uuid.UUID) ([]domain.CartItem, error) -DeleteCartItem(ctx context.Context, id uuid.UUID, buyerID uuid.UUID) error -DeleteCartItemByProduct(ctx context.Context, buyerID, productID uuid.UUID) error -ClearCart(ctx context.Context, buyerID uuid.UUID) error -ReplaceCart(ctx context.Context, buyerID uuid.UUID, items []domain.CartItem) error + AddCartItem(ctx context.Context, item *domain.CartItem) (*domain.CartItem, error) + ListCartItems(ctx context.Context, buyerID uuid.UUID) ([]domain.CartItem, error) + DeleteCartItem(ctx context.Context, id uuid.UUID, buyerID uuid.UUID) error + DeleteCartItemByProduct(ctx context.Context, buyerID, productID uuid.UUID) error + ClearCart(ctx context.Context, buyerID uuid.UUID) error + ReplaceCart(ctx context.Context, buyerID uuid.UUID, items []domain.CartItem) error -CreateReview(ctx context.Context, review *domain.Review) error -GetCompanyRating(ctx context.Context, companyID uuid.UUID) (*domain.CompanyRating, error) -ListReviews(ctx context.Context, filter domain.ReviewFilter) ([]domain.Review, int64, error) + CreateReview(ctx context.Context, review *domain.Review) error + GetCompanyRating(ctx context.Context, companyID uuid.UUID) (*domain.CompanyRating, error) + ListReviews(ctx context.Context, filter domain.ReviewFilter) ([]domain.Review, int64, error) -SellerDashboard(ctx context.Context, sellerID uuid.UUID) (*domain.SellerDashboard, error) -AdminDashboard(ctx context.Context, since time.Time) (*domain.AdminDashboard, error) + SellerDashboard(ctx context.Context, sellerID uuid.UUID) (*domain.SellerDashboard, error) + AdminDashboard(ctx context.Context, since time.Time) (*domain.AdminDashboard, error) -GetShippingSettings(ctx context.Context, vendorID uuid.UUID) (*domain.ShippingSettings, error) -UpsertShippingSettings(ctx context.Context, settings *domain.ShippingSettings) error -ListShipments(ctx context.Context, filter domain.ShipmentFilter) ([]domain.Shipment, int64, error) + GetShippingSettings(ctx context.Context, vendorID uuid.UUID) (*domain.ShippingSettings, error) + UpsertShippingSettings(ctx context.Context, settings *domain.ShippingSettings) error + ListShipments(ctx context.Context, filter domain.ShipmentFilter) ([]domain.Shipment, int64, error) -CreateDocument(ctx context.Context, doc *domain.CompanyDocument) error -ListDocuments(ctx context.Context, companyID uuid.UUID) ([]domain.CompanyDocument, error) -RecordLedgerEntry(ctx context.Context, entry *domain.LedgerEntry) error -GetLedger(ctx context.Context, companyID uuid.UUID, limit, offset int) ([]domain.LedgerEntry, int64, error) -GetBalance(ctx context.Context, companyID uuid.UUID) (int64, error) -CreateWithdrawal(ctx context.Context, withdrawal *domain.Withdrawal) error -ListWithdrawals(ctx context.Context, companyID uuid.UUID) ([]domain.Withdrawal, error) + CreateDocument(ctx context.Context, doc *domain.CompanyDocument) error + ListDocuments(ctx context.Context, companyID uuid.UUID) ([]domain.CompanyDocument, error) + RecordLedgerEntry(ctx context.Context, entry *domain.LedgerEntry) error + GetLedger(ctx context.Context, companyID uuid.UUID, limit, offset int) ([]domain.LedgerEntry, int64, error) + GetBalance(ctx context.Context, companyID uuid.UUID) (int64, error) + CreateWithdrawal(ctx context.Context, withdrawal *domain.Withdrawal) error + ListWithdrawals(ctx context.Context, companyID uuid.UUID) ([]domain.Withdrawal, error) -// Stock Reservations -ReserveStock(ctx context.Context, res *domain.StockReservation) error -CompleteReservation(ctx context.Context, reservationID uuid.UUID) error -ExpireReservations(ctx context.Context) error -GetActiveReservations(ctx context.Context, inventoryItemID uuid.UUID) (int64, error) + // Stock Reservations + ReserveStock(ctx context.Context, res *domain.StockReservation) error + CompleteReservation(ctx context.Context, reservationID uuid.UUID) error + ExpireReservations(ctx context.Context) error + GetActiveReservations(ctx context.Context, inventoryItemID uuid.UUID) (int64, error) -GetPaymentGatewayConfig(ctx context.Context, provider string) (*domain.PaymentGatewayConfig, error)UpsertPaymentGatewayConfig(ctx context.Context, config *domain.PaymentGatewayConfig) error -GetSellerPaymentAccount(ctx context.Context, sellerID uuid.UUID) (*domain.SellerPaymentAccount, error) -UpsertSellerPaymentAccount(ctx context.Context, account *domain.SellerPaymentAccount) error + GetPaymentGatewayConfig(ctx context.Context, provider string) (*domain.PaymentGatewayConfig, error) + UpsertPaymentGatewayConfig(ctx context.Context, config *domain.PaymentGatewayConfig) error + GetSellerPaymentAccount(ctx context.Context, sellerID uuid.UUID) (*domain.SellerPaymentAccount, error) + UpsertSellerPaymentAccount(ctx context.Context, account *domain.SellerPaymentAccount) error -CreateAddress(ctx context.Context, address *domain.Address) error -ListAddresses(ctx context.Context, entityID uuid.UUID) ([]domain.Address, error) -GetAddress(ctx context.Context, id uuid.UUID) (*domain.Address, error) -UpdateAddress(ctx context.Context, address *domain.Address) error -DeleteAddress(ctx context.Context, id uuid.UUID) error -ListAllAddresses(ctx context.Context) ([]domain.Address, error) + CreateAddress(ctx context.Context, address *domain.Address) error + ListAddresses(ctx context.Context, entityID uuid.UUID) ([]domain.Address, error) + ListAllAddresses(ctx context.Context) ([]domain.Address, error) + GetAddress(ctx context.Context, id uuid.UUID) (*domain.Address, error) + UpdateAddress(ctx context.Context, address *domain.Address) error + DeleteAddress(ctx context.Context, id uuid.UUID) error GeocodeAllAddresses(ctx context.Context) (int, error) + StartStockCleanupWorker(ctx context.Context) -ListManufacturers(ctx context.Context) ([]string, error) -ListCategories(ctx context.Context) ([]string, error) -GetProductByEAN(ctx context.Context, ean string) (*domain.Product, error) + ListManufacturers(ctx context.Context) ([]string, error) + ListCategories(ctx context.Context) ([]string, error) + GetProductByEAN(ctx context.Context, ean string) (*domain.Product, error) } // PaymentGateway abstracts the payment provider (Mercado Pago, Asaas, etc.). -// Implementations live in internal/infrastructure/payments/. type PaymentGateway interface { CreatePreference(ctx context.Context, order *domain.Order, payer *domain.User, sellerAcc *domain.SellerPaymentAccount) (*domain.PaymentPreference, error) CreatePayment(ctx context.Context, order *domain.Order, token, issuerID, paymentMethodID string, installments int, payer *domain.User, sellerAcc *domain.SellerPaymentAccount) (*domain.PaymentResult, error) @@ -109,61 +109,70 @@ type PaymentGateway interface { CreateBoletoPayment(ctx context.Context, order *domain.Order, payer *domain.User) (*domain.BoletoPaymentResult, error) GetPaymentStatus(ctx context.Context, paymentID string) (*domain.PaymentWebhookEvent, error) } + // Service orchestrates all business use cases. -// Methods are split across domain-specific files in this package: -// - auth_usecase.go – authentication, JWT, password reset -// - company_usecase.go – company registration, KYC, shipping options -// - product_usecase.go – catalogue, inventory management -// - order_usecase.go – order lifecycle and state machine -// - shipping_usecase.go – shipping calculation and configuration -// - cart_usecase.go – cart operations and B2B discounts -// - payment_usecase.go – payment preferences and webhook handling -// - review_usecase.go – buyer reviews and ratings -// - user_usecase.go – user CRUD -// - address_usecase.go – address management -// - dashboard_usecase.go – seller and admin KPI dashboards type Service struct { -repo Repository -pay PaymentGateway -mapbox *mapbox.Client -notify notifications.NotificationService -marketplaceCommission float64 -buyerFeeRate float64 -jwtSecret []byte -tokenTTL time.Duration -passwordPepper string + repo Repository + pay PaymentGateway + mapbox *mapbox.Client + notify notifications.NotificationService + marketplaceCommission float64 + buyerFeeRate float64 + jwtSecret []byte + tokenTTL time.Duration + passwordPepper string } const ( -// passwordResetTTL is the validity window for password-reset JWTs. -passwordResetTTL = 30 * time.Minute + // passwordResetTTL is the validity window for password-reset JWTs. + passwordResetTTL = 30 * time.Minute ) // NewService wires all dependencies and returns a ready-to-use Service. func NewService( -repo Repository, -pg PaymentGateway, -mb *mapbox.Client, -notify notifications.NotificationService, -commission, buyerFeeRate float64, -jwtSecret string, -tokenTTL time.Duration, -pepper string, + repo Repository, + pg PaymentGateway, + mb *mapbox.Client, + notify notifications.NotificationService, + commission, buyerFeeRate float64, + jwtSecret string, + tokenTTL time.Duration, + pepper string, ) *Service { -return &Service{ -repo: repo, -pay: pg, -mapbox: mb, -notify: notify, -marketplaceCommission: commission, -buyerFeeRate: buyerFeeRate, -jwtSecret: []byte(jwtSecret), -tokenTTL: tokenTTL, -passwordPepper: pepper, -} + return &Service{ + repo: repo, + pay: pg, + mapbox: mb, + notify: notify, + marketplaceCommission: commission, + buyerFeeRate: buyerFeeRate, + jwtSecret: []byte(jwtSecret), + tokenTTL: tokenTTL, + passwordPepper: pepper, + } } // GetNotificationService exposes the notification service for push handlers. func (s *Service) GetNotificationService() notifications.NotificationService { -return s.notify + return s.notify +} + +// DeleteAddress handles address removal logic. +func (s *Service) DeleteAddress(ctx context.Context, id uuid.UUID) error { + return s.repo.DeleteAddress(ctx, id) +} + +// ListManufacturers returns manufacturers from the repo. +func (s *Service) ListManufacturers(ctx context.Context) ([]string, error) { + return s.repo.ListManufacturers(ctx) +} + +// ListCategories returns categories from the repo. +func (s *Service) ListCategories(ctx context.Context) ([]string, error) { + return s.repo.ListCategories(ctx) +} + +// GetProductByEAN finds a product by its EAN code. +func (s *Service) GetProductByEAN(ctx context.Context, ean string) (*domain.Product, error) { + return s.repo.GetProductByEAN(ctx, ean) }