From 77c678dd7a16c06566338b7ba5832a7974847d70 Mon Sep 17 00:00:00 2001 From: nquidox Date: Fri, 26 Sep 2025 20:16:21 +0300 Subject: [PATCH] get prices for single entity --- internal/api/merch/controller.go | 39 +++++++++++++++++++++- internal/api/merch/repository.go | 25 +++++++++++++++ internal/api/merch/service.go | 55 ++++++++++++++++++++++++-------- 3 files changed, 105 insertions(+), 14 deletions(-) diff --git a/internal/api/merch/controller.go b/internal/api/merch/controller.go index fa8ecdd..2e31a66 100644 --- a/internal/api/merch/controller.go +++ b/internal/api/merch/controller.go @@ -32,6 +32,7 @@ func (h *Handler) RegisterRoutes(r *gin.RouterGroup, authMW gin.HandlerFunc, ref chartsGroup := r.Group("/prices", authMW) chartsGroup.GET("", h.controller.getChartsPrices) + chartsGroup.GET("/:uuid", h.controller.getDistinctPrices) } @@ -196,7 +197,7 @@ func (co *controller) deleteMerch(c *gin.Context) { // @Description Получить цены мерча за период // @Tags Merch // @Security BearerAuth -// @Param days query string false "merch_uuid" +// @Param days query string false "period in days" // @Success 200 {array} PricesResponse // @Failure 400 {object} responses.ErrorResponse400 // @Failure 500 {object} responses.ErrorResponse500 @@ -220,3 +221,39 @@ func (co *controller) getChartsPrices(c *gin.Context) { c.JSON(http.StatusOK, response) } + +// @Summary Получить перепады цен мерча за период по его merch_uuid +// @Description Получить перепады цен мерча за период по его merch_uuid +// @Tags Merch +// @Security BearerAuth +// @Param uuid path string true "merch_uuid" +// @Param days query string false "period in days" +// @Success 200 {object} PricesResponse +// @Failure 400 {object} responses.ErrorResponse400 +// @Failure 500 {object} responses.ErrorResponse500 +// @Router /prices/{uuid} [get] +func (co *controller) getDistinctPrices(c *gin.Context) { + daysQuery := strings.ToLower(c.DefaultQuery("days", "")) + + userUuid, err := co.utils.GetUserUuidFromContext(c) + if err != nil { + c.JSON(http.StatusInternalServerError, responses.ErrorResponse500{Error: err.Error()}) + log.WithError(err).Error("Merch | Failed to get user uuid from context") + return + } + + merchUuid := c.Param("uuid") + if merchUuid == "" { + c.JSON(http.StatusBadRequest, responses.ErrorResponse400{Error: "MerchUuid is empty"}) + return + } + + response, err := co.service.getDistinctPrices(userUuid, merchUuid, daysQuery) + if err != nil { + c.JSON(http.StatusBadRequest, responses.ErrorResponse500{Error: err.Error()}) + log.WithError(err).Error("Merch | Failed to get single merch") + return + } + + c.JSON(http.StatusOK, response) +} diff --git a/internal/api/merch/repository.go b/internal/api/merch/repository.go index e1aebef..e7a17e9 100644 --- a/internal/api/merch/repository.go +++ b/internal/api/merch/repository.go @@ -34,6 +34,7 @@ type repository interface { type prices interface { getPricesWithDays(userUuid string, period time.Time) ([]Price, error) + getDistinctPrices(userUuid, merchUuid string, period time.Time) (prices []Price, err error) } func (r *Repo) addMerch(bundle merchBundle) error { @@ -198,3 +199,27 @@ func (r *Repo) getPricesWithDays(userUuid string, period time.Time) (prices []Pr } return prices, nil } + +func (r *Repo) getDistinctPrices(userUuid, merchUuid string, period time.Time) (prices []Price, err error) { + err = r.db.Raw(` + SELECT price, created_at, origin + FROM ( + SELECT DISTINCT ON (price) price, created_at, origin + FROM prices + WHERE merch_uuid = ? + AND created_at > ? + AND deleted_at IS NULL + AND EXISTS ( + SELECT 1 + FROM merch + WHERE merch_uuid = ? + AND user_uuid = ? + ) + ) AS up + ORDER BY created_at;`, merchUuid, period, merchUuid, userUuid).Scan(&prices).Error + + if err != nil { + return nil, err + } + return prices, nil +} diff --git a/internal/api/merch/service.go b/internal/api/merch/service.go index ae9384c..0e17c8d 100644 --- a/internal/api/merch/service.go +++ b/internal/api/merch/service.go @@ -4,7 +4,6 @@ import ( "database/sql" "errors" "github.com/google/uuid" - "strconv" "time" ) @@ -98,18 +97,7 @@ func (s *service) getPrices(userUuid string, days string) ([]PricesResponse, err }) } - daysInt, err := strconv.Atoi(days) - if err != nil { - daysInt = 7 - } - - if daysInt > 365 { - daysInt = 7 - } - - period := time.Now().UTC().Add(-(time.Duration(daysInt) * time.Hour * 24)) - - pricesList, err := s.repo.getPricesWithDays(userUuid, period) + pricesList, err := s.repo.getPricesWithDays(userUuid, getPeriod(days)) if err != nil { return nil, err } @@ -142,3 +130,44 @@ func (s *service) getPrices(userUuid string, days string) ([]PricesResponse, err return response, nil } + +func (s *service) getDistinctPrices(userUuid, merchUuid, days string) (PricesResponse, error) { + result, err := s.repo.getDistinctPrices(userUuid, merchUuid, getPeriod(days)) + if err != nil { + return PricesResponse{}, err + } + + if result == nil { + return PricesResponse{}, errors.New("no prices found") + } + + originSurugaya := OriginWithPrices{ + Origin: surugaya, + Prices: []PriceEntry{}, + } + + originMandarake := OriginWithPrices{ + Origin: mandarake, + Prices: []PriceEntry{}, + } + + for _, item := range result { + switch item.Origin { + case surugaya: + originSurugaya.Prices = append(originSurugaya.Prices, PriceEntry{ + CreatedAt: item.CreatedAt.Unix(), + Value: item.Price, + }) + case mandarake: + originMandarake.Prices = append(originMandarake.Prices, PriceEntry{ + CreatedAt: item.CreatedAt.Unix(), + Value: item.Price, + }) + } + } + + return PricesResponse{ + MerchUuid: merchUuid, + Origins: []OriginWithPrices{originSurugaya, originMandarake}, + }, nil +}