From ba78dd9772c8b321bb6222ab074e2b7c4e3c249e Mon Sep 17 00:00:00 2001 From: nquidox Date: Wed, 24 Sep 2025 20:28:36 +0300 Subject: [PATCH] added get merch prices method --- internal/api/merch/controller.go | 39 ++++++++++++++++++-- internal/api/merch/dto.go | 18 ++++++++++ internal/api/merch/model.go | 20 ++++++++--- internal/api/merch/repository.go | 33 +++++++++++++++++ internal/api/merch/service.go | 61 ++++++++++++++++++++++++++++++++ 5 files changed, 163 insertions(+), 8 deletions(-) diff --git a/internal/api/merch/controller.go b/internal/api/merch/controller.go index 167fd8d..fa8ecdd 100644 --- a/internal/api/merch/controller.go +++ b/internal/api/merch/controller.go @@ -6,6 +6,7 @@ import ( "merch-parser-api/internal/interfaces" "merch-parser-api/pkg/responses" "net/http" + "strings" ) type controller struct { @@ -29,6 +30,9 @@ func (h *Handler) RegisterRoutes(r *gin.RouterGroup, authMW gin.HandlerFunc, ref merchGroup.PUT("/", h.controller.updateMerch) merchGroup.DELETE("/:uuid", h.controller.deleteMerch) + chartsGroup := r.Group("/prices", authMW) + chartsGroup.GET("", h.controller.getChartsPrices) + } // @Summary Добавить новый мерч @@ -103,9 +107,9 @@ func (co *controller) getSingleMerch(c *gin.Context) { // @Description Получить все записи мерча // @Tags Merch // @Security BearerAuth -// @Success 200 {array} ListResponse -// @Failure 400 {object} responses.ErrorResponse400 -// @Failure 500 {object} responses.ErrorResponse500 +// @Success 200 {array} ListResponse +// @Failure 400 {object} responses.ErrorResponse400 +// @Failure 500 {object} responses.ErrorResponse500 // @Router /merch/ [get] func (co *controller) getAllMerch(c *gin.Context) { userUuid, err := co.utils.GetUserUuidFromContext(c) @@ -187,3 +191,32 @@ func (co *controller) deleteMerch(c *gin.Context) { c.Status(http.StatusOK) } + +// @Summary Получить цены мерча за период +// @Description Получить цены мерча за период +// @Tags Merch +// @Security BearerAuth +// @Param days query string false "merch_uuid" +// @Success 200 {array} PricesResponse +// @Failure 400 {object} responses.ErrorResponse400 +// @Failure 500 {object} responses.ErrorResponse500 +// @Router /prices [get] +func (co *controller) getChartsPrices(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 + } + + response, err := co.service.getPrices(userUuid, 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/dto.go b/internal/api/merch/dto.go index 39f8ef4..f8d1630 100644 --- a/internal/api/merch/dto.go +++ b/internal/api/merch/dto.go @@ -1,5 +1,7 @@ package merch +import "time" + type merchBundle struct { Merch *Merch Surugaya *Surugaya @@ -30,3 +32,19 @@ type ListResponse struct { MerchUuid string `json:"merch_uuid"` Name string `json:"name"` } + +type PriceEntry struct { + CreatedAt time.Time `json:"created_at"` + Value int `json:"value"` +} + +type OriginWithPrices struct { + Origin Origin `json:"origin"` + Prices []PriceEntry +} + +type PricesResponse struct { + Name string `json:"name"` + MerchUuid string `json:"merch_uuid"` + Origins []OriginWithPrices `json:"origins"` +} diff --git a/internal/api/merch/model.go b/internal/api/merch/model.go index dddae64..ed7e550 100644 --- a/internal/api/merch/model.go +++ b/internal/api/merch/model.go @@ -7,11 +7,11 @@ import ( type Merch struct { Id uint `json:"id" gorm:"primary_key"` - CreatedAt time.Time `json:"created_at" gorm:"created_at"` - UpdatedAt sql.NullTime `json:"updated_at" gorm:"updated_at"` - DeletedAt sql.NullTime `json:"deleted_at" gorm:"deleted_at"` - MerchUuid string `json:"merch_uuid" gorm:"type:varchar(36);unique_index"` - UserUuid string `json:"user_uuid" gorm:"type:varchar(36)"` + CreatedAt time.Time `json:"created_at" gorm:"column:created_at"` + UpdatedAt sql.NullTime `json:"updated_at" gorm:"column:updated_at"` + DeletedAt sql.NullTime `json:"deleted_at" gorm:"column:deleted_at"` + MerchUuid string `json:"merch_uuid" gorm:"column:merch_uuid"` + UserUuid string `json:"user_uuid" gorm:"column:user_uuid"` Name string `json:"name" gorm:"column:name"` } @@ -41,3 +41,13 @@ type Mandarake struct { func (Mandarake) TableName() string { return "origin_mandarake" } + +type Price struct { + Id uint `json:"id" gorm:"primary_key"` + CreatedAt time.Time `json:"created_at" gorm:"column:created_at"` + UpdatedAt sql.NullTime `json:"updated_at" gorm:"column:updated_at"` + DeletedAt sql.NullTime `json:"deleted_at" gorm:"column:deleted_at"` + MerchUuid string `json:"merch_uuid" gorm:"column:merch_uuid"` + Price int `json:"price" gorm:"column:price"` + Origin Origin `json:"origin" gorm:"column:origin"` +} diff --git a/internal/api/merch/repository.go b/internal/api/merch/repository.go index 3258099..2592e42 100644 --- a/internal/api/merch/repository.go +++ b/internal/api/merch/repository.go @@ -26,6 +26,14 @@ type repository interface { updateMerch(payload MerchDTO, userUuid string) error deleteMerch(userUuid, merchUuid string) error + + getAllUserMerch(userUuid string) ([]Merch, error) + + prices +} + +type prices interface { + getPricesWithDays(userUuid string, period time.Time) ([]Price, error) } func (r *Repo) addMerch(bundle merchBundle) error { @@ -161,3 +169,28 @@ func (r *Repo) deleteMerch(userUuid, merchUuid string) error { return nil } + +func (r *Repo) getAllUserMerch(userUuid string) (merchList []Merch, err error) { + err = r.db.Model(&Merch{}).Where("user_uuid = ?", userUuid).Find(&merchList).Error + if err != nil { + return nil, err + } + return merchList, nil +} + +func (r *Repo) getPricesWithDays(userUuid string, period time.Time) (prices []Price, err error) { + err = r.db.Raw(` + SELECT p.created_at, p.merch_uuid, p.price, p.origin + FROM prices AS p + JOIN merch AS m ON m.merch_uuid = p.merch_uuid + where m.user_uuid = ? + and p.created_at > ? + and p.deleted_at IS NULL + order by p.created_at desc + `, userUuid, period).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 a2d4246..4470e43 100644 --- a/internal/api/merch/service.go +++ b/internal/api/merch/service.go @@ -4,6 +4,7 @@ import ( "database/sql" "errors" "github.com/google/uuid" + "strconv" "time" ) @@ -77,3 +78,63 @@ func (s *service) updateMerch(payload MerchDTO, userUuid string) error { func (s *service) deleteMerch(userUuid, merchUuid string) error { return s.repo.deleteMerch(userUuid, merchUuid) } + +func (s *service) getPrices(userUuid string, days string) ([]PricesResponse, error) { + merchList, err := s.repo.getAllUserMerch(userUuid) + if err != nil { + return nil, err + } + + if len(merchList) == 0 { + return nil, errors.New("no merch found") + } + + var response []PricesResponse + for _, item := range merchList { + response = append(response, PricesResponse{ + MerchUuid: item.MerchUuid, + Name: item.Name, + Origins: []OriginWithPrices{}, + }) + } + + daysInt, err := strconv.Atoi(days) + if err != nil { + daysInt = 7 + } + + period := time.Now().UTC().Add(-(time.Duration(daysInt) * time.Hour * 24)) + + pricesList, err := s.repo.getPricesWithDays(userUuid, period) + if err != nil { + return nil, err + } + + pricesMap := make(map[string]map[Origin][]PriceEntry) + for _, item := range pricesList { + if _, ok := pricesMap[item.MerchUuid]; !ok { + pricesMap[item.MerchUuid] = make(map[Origin][]PriceEntry) + } + + pricesMap[item.MerchUuid][item.Origin] = append(pricesMap[item.MerchUuid][item.Origin], PriceEntry{ + CreatedAt: item.CreatedAt, + Value: item.Price, + }) + } + + for i := range response { + originSurugaya := OriginWithPrices{ + Origin: surugaya, + Prices: pricesMap[response[i].MerchUuid][surugaya], + } + response[i].Origins = append(response[i].Origins, originSurugaya) + + originMandarake := OriginWithPrices{ + Origin: mandarake, + Prices: pricesMap[response[i].MerchUuid][mandarake], + } + response[i].Origins = append(response[i].Origins, originMandarake) + } + + return response, nil +}