// Package service for service's package service import ( "context" "database/sql" "errors" "log/slog" "math" "steam_analyzer/internal/domain" "steam_analyzer/internal/repository" "steam_analyzer/internal/utils" "steam_analyzer/internal/utils/slogutils" "steam_analyzer/pkg/postgresql" "time" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgconn" ) type ItemRepository interface { AddItem(ctx context.Context, item domain.DBItem) error AddItemHistory(ctx context.Context, dbItemHistory domain.DBItemHistory) error GetItemByClassID(ctx context.Context, classID string) (*domain.SteamItem, error) GetItemPriceHistoryByID(ctx context.Context, itemID uuid.UUID, limit int, offset int) ([]domain.ItemPriceHistory, error) GetItemsWithLastPriceByName(ctx context.Context, name string) ([]repository.ItemWithLastPrice, error) } type ItemService struct { logger *slog.Logger itemRepo ItemRepository } func NewItemService(logger *slog.Logger, itemRepo ItemRepository) *ItemService { return &ItemService{ logger: logger, itemRepo: itemRepo, } } func (s ItemService) AddItemWithHistory(ctx context.Context, item domain.SteamItem) error { searchedItem, err := s.itemRepo.GetItemByClassID(ctx, item.ClassID) if err != nil { switch { case errors.Is(err, sql.ErrNoRows): s.logger.Info("Item is new, creating it") default: return err } } if searchedItem != nil { item.ID = searchedItem.ID } else { item.ID = uuid.New() err = s.itemRepo.AddItem(ctx, item.ToDB()) if err != nil { if pgErr, ok := err.(*pgconn.PgError); ok && pgErr.Code == postgresql.ErrConstraintUnique { s.logger.Error("Error while adding item", slog.String("error", "Item already exists")) } else { s.logger.Error("Error while adding item", slogutils.Error(err)) return err } } } err = s.itemRepo.AddItemHistory(ctx, item.ToDBHistory()) if err != nil { s.logger.Error("Error while adding item history", slogutils.Error(sql.ErrNoRows)) return err } return nil } func (s ItemService) GetItemWithPriceHistoryByClassID(ctx context.Context, classID string, limit int, offset int) (*domain.SteamItem, error) { steamItem, err := s.itemRepo.GetItemByClassID(ctx, classID) if err != nil { s.logger.Error("Error while getting item by class id", slogutils.Error(err)) return nil, err } itemPriceHistory, err := s.itemRepo.GetItemPriceHistoryByID(ctx, steamItem.ID, limit, offset) if err != nil { s.logger.Error("Error while getting item price history by id", slogutils.Error(err)) return nil, err } steamItem.History = itemPriceHistory return steamItem, nil } func (s ItemService) GetAllItemsWithLastPrice(ctx context.Context, itemName string) ([]repository.ItemWithLastPrice, error) { s.logger.Info("ItemService.GetAllItemsWithLastPrice", slog.String("item name is", itemName)) items, err := s.itemRepo.GetItemsWithLastPriceByName(ctx, itemName) if err != nil { s.logger.Error("Error while getting item with last price", slogutils.Error(err)) return nil, err } for i, item := range items { items[i].Price = formatPrice(item.Price) items[i].Date = formatDate(item.Date) } return items, nil } func formatPrice(price float64) float64 { formattedPrice := price / 100 * utils.CURRENCY_RUB formattedPrice = math.Round(formattedPrice*100) / 100 return formattedPrice } func formatDate(date time.Time) time.Time { location, _ := time.LoadLocation("Europe/Moscow") foramettedDate := date.In(location) return foramettedDate }