get prices methods
This commit is contained in:
parent
c772f0a4d2
commit
9e97f082fa
4 changed files with 324 additions and 14 deletions
|
|
@ -38,6 +38,10 @@ func (h *Handler) RegisterRoutes(r *gin.RouterGroup) {
|
||||||
originsGroup.GET("", h.controller.getOrigins)
|
originsGroup.GET("", h.controller.getOrigins)
|
||||||
originsGroup.DELETE("", h.controller.deleteOrigin)
|
originsGroup.DELETE("", h.controller.deleteOrigin)
|
||||||
|
|
||||||
|
chartsGroup := r.Group("/prices")
|
||||||
|
chartsGroup.GET("", h.controller.getChartsPrices)
|
||||||
|
chartsGroup.GET("/:uuid", h.controller.getDistinctPrices)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// create godoc
|
// create godoc
|
||||||
|
|
@ -105,13 +109,13 @@ func (co *controller) getMany(c *gin.Context) {
|
||||||
// @Description Update merch general info (except extra data)
|
// @Description Update merch general info (except extra data)
|
||||||
// @Tags Merch
|
// @Tags Merch
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param uuid path string true "merch uuid"
|
// @Param uuid path string true "merch uuid"
|
||||||
// @Param payload body updateMerchDTO true "payload"
|
// @Param payload body updateMerchDTO true "payload"
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} merchDTO
|
// @Success 200 {object} merchDTO
|
||||||
// @Failure 400 {object} responses.BadRequest
|
// @Failure 400 {object} responses.BadRequest
|
||||||
// @Failure 401 {object} responses.Unauthorized
|
// @Failure 401 {object} responses.Unauthorized
|
||||||
// @Failure 500 {object} responses.InternalServerError
|
// @Failure 500 {object} responses.InternalServerError
|
||||||
// @Router /merch/{uuid} [PUT]
|
// @Router /merch/{uuid} [PUT]
|
||||||
func (co *controller) updateMerch(c *gin.Context) {
|
func (co *controller) updateMerch(c *gin.Context) {
|
||||||
merchUuid := c.Param("id")
|
merchUuid := c.Param("id")
|
||||||
|
|
@ -150,13 +154,13 @@ func (co *controller) updateMerch(c *gin.Context) {
|
||||||
// @Description Update ONLY merch extra data
|
// @Description Update ONLY merch extra data
|
||||||
// @Tags Merch
|
// @Tags Merch
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param uuid path string true "merch uuid"
|
// @Param uuid path string true "merch uuid"
|
||||||
// @Param payload body extraDataDTO true "payload"
|
// @Param payload body extraDataDTO true "payload"
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} extraDataDTO
|
// @Success 200 {object} extraDataDTO
|
||||||
// @Failure 400 {object} responses.BadRequest
|
// @Failure 400 {object} responses.BadRequest
|
||||||
// @Failure 401 {object} responses.Unauthorized
|
// @Failure 401 {object} responses.Unauthorized
|
||||||
// @Failure 500 {object} responses.InternalServerError
|
// @Failure 500 {object} responses.InternalServerError
|
||||||
// @Router /merch/extra/{uuid} [PUT]
|
// @Router /merch/extra/{uuid} [PUT]
|
||||||
func (co *controller) updateExtraData(c *gin.Context) {
|
func (co *controller) updateExtraData(c *gin.Context) {
|
||||||
merchUuid := c.Param("id")
|
merchUuid := c.Param("id")
|
||||||
|
|
@ -303,3 +307,66 @@ func (co *controller) deleteOrigin(c *gin.Context) {
|
||||||
logDebug(controllerLogHeader, "delete origin success")
|
logDebug(controllerLogHeader, "delete origin success")
|
||||||
c.Status(http.StatusNoContent)
|
c.Status(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getChartsPrices godoc
|
||||||
|
//
|
||||||
|
// @Summary Получить цены мерча за период
|
||||||
|
// @Description Получить цены мерча за период
|
||||||
|
// @Tags Merch
|
||||||
|
// @Security BearerAuth
|
||||||
|
// @Produce json
|
||||||
|
// @Param days query string false "period in days"
|
||||||
|
// @Success 200 {array} PricesResponse
|
||||||
|
// @Failure 400 {object} responses.BadRequest
|
||||||
|
// @Failure 401 {object} responses.Unauthorized
|
||||||
|
// @Failure 500 {object} responses.InternalServerError
|
||||||
|
// @Router /prices [get]
|
||||||
|
func (co *controller) getChartsPrices(c *gin.Context) {
|
||||||
|
response, err := co.service.getPrices(c, getUserId(c), getDays(c))
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, responses.InternalServerError{Error: err.Error()})
|
||||||
|
logErr(controllerLogHeader, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDistinctPrices godoc
|
||||||
|
//
|
||||||
|
// @Summary Получить перепады цен мерча за период по его merch_uuid
|
||||||
|
// @Description Получить перепады цен мерча за период по его merch_uuid
|
||||||
|
// @Tags Merch
|
||||||
|
// @Security BearerAuth
|
||||||
|
// @Produce json
|
||||||
|
// @Param uuid path string true "merch_uuid"
|
||||||
|
// @Param days query string false "period in days"
|
||||||
|
// @Success 200 {object} PricesResponse
|
||||||
|
// @Success 204
|
||||||
|
// @Failure 400 {object} responses.BadRequest
|
||||||
|
// @Failure 401 {object} responses.Unauthorized
|
||||||
|
// @Failure 500 {object} responses.InternalServerError
|
||||||
|
// @Router /prices/{uuid} [get]
|
||||||
|
func (co *controller) getDistinctPrices(c *gin.Context) {
|
||||||
|
merchUuid := c.Param("uuid")
|
||||||
|
if merchUuid == "" {
|
||||||
|
err := errors.New("MerchUuid is empty")
|
||||||
|
c.JSON(http.StatusBadRequest, responses.BadRequest{Error: err.Error()})
|
||||||
|
logErr(controllerLogHeader, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := co.service.getDistinctPrices(c, getUserId(c), merchUuid, getDays(c))
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, responses.InternalServerError{Error: err.Error()})
|
||||||
|
logErr(controllerLogHeader, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if response == nil {
|
||||||
|
c.Status(http.StatusNoContent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, response)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,3 +45,20 @@ type extraDataDTO struct {
|
||||||
MerchUuid string `json:"merch_uuid"`
|
MerchUuid string `json:"merch_uuid"`
|
||||||
Links []originLink `json:"links"`
|
Links []originLink `json:"links"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prices dtos
|
||||||
|
type PriceEntry struct {
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
Value int `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OriginWithPrices struct {
|
||||||
|
Origin string `json:"origin"`
|
||||||
|
Prices []PriceEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
type PricesResponse struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
MerchUuid string `json:"merch_uuid"`
|
||||||
|
Origins []OriginWithPrices `json:"origins"`
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ type Repository interface {
|
||||||
|
|
||||||
getMerchIdByUuid(ctx context.Context, userId int64, uuid string) (int64, error)
|
getMerchIdByUuid(ctx context.Context, userId int64, uuid string) (int64, error)
|
||||||
getMerchUuidMap(ctx context.Context, merchUuids []string) (map[string]int64, error)
|
getMerchUuidMap(ctx context.Context, merchUuids []string) (map[string]int64, error)
|
||||||
|
getAllUserMerch(ctx context.Context, userId int64) ([]Merch, error)
|
||||||
|
|
||||||
updateMerch(ctx context.Context, userId int64, merch *updateMerchDTO) (*merchDTO, error)
|
updateMerch(ctx context.Context, userId int64, merch *updateMerchDTO) (*merchDTO, error)
|
||||||
updateExtraData(ctx context.Context, merchId int64, insertData []ExtraData) ([]ExtraData, error)
|
updateExtraData(ctx context.Context, merchId int64, insertData []ExtraData) ([]ExtraData, error)
|
||||||
|
|
@ -39,6 +40,8 @@ type Origins interface {
|
||||||
|
|
||||||
type Prices interface {
|
type Prices interface {
|
||||||
insertPrices(ctx context.Context, prices []Price) error
|
insertPrices(ctx context.Context, prices []Price) error
|
||||||
|
getPricesWithDays(ctx context.Context, userId int64, days time.Time) ([]Price, error)
|
||||||
|
getDistinctPrices(ctx context.Context, userId int64, merchUuid string, days time.Time) ([]Price, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Tasks interface {
|
type Tasks interface {
|
||||||
|
|
@ -219,6 +222,32 @@ func (r *repo) getMerchUuidMap(ctx context.Context, merchUuids []string) (map[st
|
||||||
return merchUuidMap, nil
|
return merchUuidMap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *repo) getAllUserMerch(ctx context.Context, userId int64) ([]Merch, error) {
|
||||||
|
var userMerch []Merch
|
||||||
|
|
||||||
|
q := `SELECT id, merch_uuid, name FROM merch WHERE user_id = $1 AND deleted_at IS NULL`
|
||||||
|
|
||||||
|
rows, err := r.db.Query(ctx, q, userId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var m Merch
|
||||||
|
if err = rows.Scan(&m.Id, &m.MerchUuid, &m.Name); err != nil {
|
||||||
|
rows.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
userMerch = append(userMerch, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.Close()
|
||||||
|
if err = rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return userMerch, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *repo) deleteOneMerchRecord(ctx context.Context, userId int64, merchUuid string, delTime time.Time) error {
|
func (r *repo) deleteOneMerchRecord(ctx context.Context, userId int64, merchUuid string, delTime time.Time) error {
|
||||||
tx, err := r.db.Begin(ctx)
|
tx, err := r.db.Begin(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -365,3 +394,78 @@ func (r *repo) getTaskData(ctx context.Context) ([]taskData, error) {
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *repo) getPricesWithDays(ctx context.Context, userId int64, days time.Time) ([]Price, error) {
|
||||||
|
q := `
|
||||||
|
SELECT mp.created_at, mp.merch_id, mp.price, mp.origin_id
|
||||||
|
FROM merch_prices AS mp
|
||||||
|
JOIN merch AS m ON m.id = mp.merch_id
|
||||||
|
WHERE m.user_id = $1
|
||||||
|
AND mp.created_at > $2
|
||||||
|
AND mp.deleted_at IS NULL
|
||||||
|
AND m.deleted_at IS NULL
|
||||||
|
`
|
||||||
|
|
||||||
|
rows, err := r.db.Query(ctx, q, userId, days)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []Price
|
||||||
|
for rows.Next() {
|
||||||
|
var p Price
|
||||||
|
if err = rows.Scan(&p.CreatedAt, &p.MerchId, &p.Price, &p.OriginId); err != nil {
|
||||||
|
rows.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, p)
|
||||||
|
}
|
||||||
|
rows.Close()
|
||||||
|
|
||||||
|
if err = rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *repo) getDistinctPrices(ctx context.Context, userId int64, merchUuid string, days time.Time) ([]Price, error) {
|
||||||
|
q := `
|
||||||
|
SELECT price, created_at, origin_id
|
||||||
|
FROM (
|
||||||
|
SELECT DISTINCT ON (price) price, created_at, origin_id
|
||||||
|
FROM merch_prices
|
||||||
|
WHERE merch_id = (
|
||||||
|
SELECT id
|
||||||
|
FROM merch
|
||||||
|
WHERE merch_uuid = $1
|
||||||
|
AND user_id = $2
|
||||||
|
AND deleted_at IS NULL
|
||||||
|
)
|
||||||
|
AND deleted_at IS NULL
|
||||||
|
AND created_at > $3
|
||||||
|
)
|
||||||
|
ORDER BY created_at;
|
||||||
|
`
|
||||||
|
fmt.Println(merchUuid, userId, days)
|
||||||
|
rows, err := r.db.Query(ctx, q, merchUuid, userId, days)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []Price
|
||||||
|
for rows.Next() {
|
||||||
|
var p Price
|
||||||
|
if err = rows.Scan(&p.Price, &p.CreatedAt, &p.OriginId); err != nil {
|
||||||
|
rows.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result = append(result, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.Close()
|
||||||
|
if err = rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,11 @@ package merch
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/google/uuid"
|
||||||
"merch-api/internal/user"
|
"merch-api/internal/user"
|
||||||
"merch-api/pkg/utils"
|
"merch-api/pkg/utils"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const serviceLogHeader string = "[Service]"
|
const serviceLogHeader string = "[Service]"
|
||||||
|
|
@ -111,6 +112,9 @@ func (s *service) createMerch(ctx context.Context, userId int64, payload *newMer
|
||||||
return s.repo.createMerch(ctx, newMerch, merchExtra)
|
return s.repo.createMerch(ctx, newMerch, merchExtra)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getOriginsMaps
|
||||||
|
// first return name:id
|
||||||
|
// second id:name
|
||||||
func (s *service) getOriginsMaps(ctx context.Context) (map[string]int64, map[int64]string, error) {
|
func (s *service) getOriginsMaps(ctx context.Context) (map[string]int64, map[int64]string, error) {
|
||||||
origins, err := s.repo.getOrigins(ctx)
|
origins, err := s.repo.getOrigins(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -197,3 +201,121 @@ func (s *service) updateExtraData(ctx context.Context, userId int64, payload *ex
|
||||||
func (s *service) deleteOneMerchRecord(ctx context.Context, userId int64, merchUuid string) error {
|
func (s *service) deleteOneMerchRecord(ctx context.Context, userId int64, merchUuid string) error {
|
||||||
return s.repo.deleteOneMerchRecord(ctx, userId, merchUuid, s.utils.TimeNowUTC())
|
return s.repo.deleteOneMerchRecord(ctx, userId, merchUuid, s.utils.TimeNowUTC())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *service) getPrices(ctx context.Context, userId int64, days int) ([]PricesResponse, error) {
|
||||||
|
fmt.Println("Enter service")
|
||||||
|
merchList, err := s.repo.getAllUserMerch(ctx, userId)
|
||||||
|
if err != nil {
|
||||||
|
logErr(serviceLogHeader, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(merchList) == 0 {
|
||||||
|
errMsg := errors.New("no merch found")
|
||||||
|
logErr(serviceLogHeader, errMsg)
|
||||||
|
return nil, errMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
merchMap := make(map[int64]Merch, len(merchList))
|
||||||
|
for _, merch := range merchList {
|
||||||
|
merchMap[merch.Id] = merch
|
||||||
|
}
|
||||||
|
|
||||||
|
var response []PricesResponse
|
||||||
|
for _, item := range merchList {
|
||||||
|
response = append(response, PricesResponse{
|
||||||
|
MerchUuid: item.MerchUuid,
|
||||||
|
Name: item.Name,
|
||||||
|
Origins: []OriginWithPrices{},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pricesList, err := s.repo.getPricesWithDays(ctx, userId, getPeriod(days))
|
||||||
|
if err != nil {
|
||||||
|
logErr(serviceLogHeader, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, originNamesMap, err := s.getOriginsMaps(ctx)
|
||||||
|
if err != nil {
|
||||||
|
logErr(serviceLogHeader, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pricesMap := make(map[string]map[string][]PriceEntry)
|
||||||
|
for _, item := range pricesList {
|
||||||
|
merchUuid := merchMap[item.MerchId].MerchUuid
|
||||||
|
|
||||||
|
if _, ok := pricesMap[merchUuid]; !ok {
|
||||||
|
pricesMap[merchUuid] = make(map[string][]PriceEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
originName := originNamesMap[item.OriginId]
|
||||||
|
pricesMap[merchUuid][originName] = append(pricesMap[merchUuid][originName], PriceEntry{
|
||||||
|
CreatedAt: item.CreatedAt.Unix(),
|
||||||
|
Value: item.Price,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range response {
|
||||||
|
for _, name := range originNamesMap {
|
||||||
|
prices := pricesMap[response[i].MerchUuid][name]
|
||||||
|
if prices == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
response[i].Origins = append(response[i].Origins, OriginWithPrices{
|
||||||
|
Origin: name,
|
||||||
|
Prices: prices,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) getDistinctPrices(ctx context.Context, userId int64, merchUuid string, days int) (*PricesResponse, error) {
|
||||||
|
result, err := s.repo.getDistinctPrices(ctx, userId, merchUuid, getPeriod(days))
|
||||||
|
if err != nil {
|
||||||
|
logErr(serviceLogHeader, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if result == nil {
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, originNamesMap, err := s.getOriginsMaps(ctx)
|
||||||
|
if err != nil {
|
||||||
|
logErr(serviceLogHeader, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response := PricesResponse{
|
||||||
|
MerchUuid: merchUuid,
|
||||||
|
Origins: []OriginWithPrices{},
|
||||||
|
}
|
||||||
|
|
||||||
|
pricesMap := make(map[string][]PriceEntry)
|
||||||
|
for _, item := range result {
|
||||||
|
originName := originNamesMap[item.OriginId]
|
||||||
|
if _, ok := pricesMap[originName]; !ok {
|
||||||
|
pricesMap[originName] = make([]PriceEntry, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pricesMap[originName] = append(pricesMap[originName], PriceEntry{
|
||||||
|
CreatedAt: item.CreatedAt.Unix(),
|
||||||
|
Value: item.Price,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, item := range pricesMap {
|
||||||
|
response.Origins = append(response.Origins, OriginWithPrices{
|
||||||
|
Origin: key,
|
||||||
|
Prices: item,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return &response, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue