mirror of
https://github.com/3ybactuk/marketplace-go-service-project.git
synced 2025-10-30 05:53:45 +03:00
166 lines
3.8 KiB
Go
166 lines
3.8 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"slices"
|
|
"sync"
|
|
|
|
"route256/cart/internal/domain/entity"
|
|
"route256/cart/internal/domain/model"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
type Repository interface {
|
|
AddItem(ctx context.Context, userID entity.UID, item *model.Item) error
|
|
GetItemsByUserID(ctx context.Context, userID entity.UID) (entity.Cart, error)
|
|
DeleteItem(ctx context.Context, userID entity.UID, sku entity.Sku) error
|
|
DeleteItemsByUserID(ctx context.Context, userID entity.UID) error
|
|
}
|
|
|
|
type ProductService interface {
|
|
GetProductBySku(ctx context.Context, sku entity.Sku) (*model.Product, error)
|
|
}
|
|
|
|
type CartService struct {
|
|
repository Repository
|
|
productService ProductService
|
|
}
|
|
|
|
func NewCartService(repository Repository, productService ProductService) *CartService {
|
|
return &CartService{
|
|
repository: repository,
|
|
productService: productService,
|
|
}
|
|
}
|
|
|
|
func (s *CartService) AddItem(ctx context.Context, userID entity.UID, item *model.Item) error {
|
|
if err := item.Validate(); err != nil {
|
|
return fmt.Errorf("invalid requested values: %w", err)
|
|
}
|
|
|
|
if userID <= 0 {
|
|
return fmt.Errorf("invalid userID")
|
|
}
|
|
|
|
_, err := s.productService.GetProductBySku(ctx, item.Product.Sku)
|
|
if err != nil {
|
|
return fmt.Errorf("productService.GetProductBySku: %w", err)
|
|
}
|
|
|
|
if err := s.repository.AddItem(ctx, userID, item); err != nil {
|
|
return fmt.Errorf("repository.AddItemToCart: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetUserCart gets all user cart's item ids, gets the item description from the product-service
|
|
// and return a list of the collected items.
|
|
// In case of failed request to product-service, return nothing and error.
|
|
//
|
|
// TODO: add worker group, BUT it's OK for now,
|
|
// assuming user does not have hundreds of different items in his cart.
|
|
func (s *CartService) GetItemsByUserID(ctx context.Context, userID entity.UID) (*model.Cart, error) {
|
|
if userID <= 0 {
|
|
return nil, fmt.Errorf("userID invalid")
|
|
}
|
|
|
|
cart, err := s.repository.GetItemsByUserID(ctx, userID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("repository.AddItemToCart: %w", err)
|
|
}
|
|
|
|
if len(cart.Items) == 0 {
|
|
return nil, model.ErrCartNotFound
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
defer cancel()
|
|
|
|
resultCart := &model.Cart{
|
|
UserID: userID,
|
|
Items: make([]*model.Item, len(cart.Items)),
|
|
TotalPrice: 0,
|
|
}
|
|
|
|
errCh := make(chan error, 1)
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
for idx, sku := range cart.Items {
|
|
wg.Add(1)
|
|
|
|
go func(sku entity.Sku, count uint32, idx int) {
|
|
defer wg.Done()
|
|
|
|
product, err := s.productService.GetProductBySku(ctx, sku)
|
|
if err != nil {
|
|
select {
|
|
case errCh <- fmt.Errorf("productService.GetProductBySku: %w", err):
|
|
case <-ctx.Done():
|
|
}
|
|
|
|
log.Error().Err(err).Msg("productService.GetProductBySku")
|
|
|
|
return
|
|
}
|
|
|
|
resultCart.Items[idx] = &model.Item{
|
|
Product: product,
|
|
Count: count,
|
|
}
|
|
resultCart.TotalPrice += uint32(product.Price) * count
|
|
}(sku, cart.ItemCount[sku], idx)
|
|
}
|
|
|
|
doneCh := make(chan struct{})
|
|
go func() {
|
|
wg.Wait()
|
|
|
|
close(doneCh)
|
|
}()
|
|
|
|
select {
|
|
case err := <-errCh:
|
|
cancel()
|
|
|
|
return nil, err
|
|
case <-doneCh:
|
|
slices.SortStableFunc(resultCart.Items, func(a, b *model.Item) int {
|
|
return int(a.Product.Sku - b.Product.Sku)
|
|
})
|
|
|
|
return resultCart, nil
|
|
}
|
|
}
|
|
|
|
func (s *CartService) DeleteItem(ctx context.Context, userID entity.UID, sku entity.Sku) error {
|
|
if userID <= 0 {
|
|
return fmt.Errorf("userID invalid")
|
|
}
|
|
|
|
if sku <= 0 {
|
|
return fmt.Errorf("sku invalid")
|
|
}
|
|
|
|
if err := s.repository.DeleteItem(ctx, userID, sku); err != nil {
|
|
return fmt.Errorf("repository.DeleteItemFromUserCart: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *CartService) DeleteItemsByUserID(ctx context.Context, userID entity.UID) error {
|
|
if userID <= 0 {
|
|
return fmt.Errorf("userID invalid")
|
|
}
|
|
|
|
if err := s.repository.DeleteItemsByUserID(ctx, userID); err != nil {
|
|
return fmt.Errorf("repository.DeleteUserCart: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|