api/internal/api/merch/controller.go
2025-10-28 20:29:14 +03:00

632 lines
22 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package merch
import (
"context"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"merch-parser-api/internal/interfaces"
"merch-parser-api/pkg/responses"
"net/http"
"strings"
"time"
)
type controller struct {
service *service
utils interfaces.Utils
expires time.Duration
}
func newController(service *service, utils interfaces.Utils, expires time.Duration) *controller {
return &controller{
service: service,
utils: utils,
expires: expires,
}
}
func (h *Handler) RegisterRoutes(r *gin.RouterGroup, authMW gin.HandlerFunc, refreshMW gin.HandlerFunc) {
merchGroup := r.Group("/merch", authMW)
merchGroup.POST("/", h.controller.addMerch)
merchGroup.GET("/:uuid", h.controller.getSingleMerch)
merchGroup.GET("/", h.controller.getAllMerch)
merchGroup.PUT("/", h.controller.updateMerch)
merchGroup.DELETE("/:uuid", h.controller.deleteMerch)
chartsGroup := r.Group("/prices", authMW)
chartsGroup.GET("", h.controller.getChartsPrices)
chartsGroup.GET("/:uuid", h.controller.getDistinctPrices)
imagesGroup := merchGroup.Group("/images")
imagesGroup.POST("/:uuid", h.controller.uploadMerchImage)
imagesGroup.GET("/:uuid", h.controller.getMerchImage)
imagesGroup.DELETE("/:uuid", h.controller.deleteMerchImage)
labelsGroup := merchGroup.Group("/labels")
labelsGroup.POST("", h.controller.createLabel)
labelsGroup.GET("", h.controller.getLabels)
labelsGroup.PUT("/:uuid", h.controller.updateLabel)
labelsGroup.DELETE("/:uuid", h.controller.deleteLabel)
labelsGroup.POST("/attach", h.controller.attachLabel)
labelsGroup.POST("/detach", h.controller.detachLabel)
}
// @Summary Добавить новый мерч
// @Description Добавить новый мерч
// @Tags Merch
// @Security BearerAuth
// @Accept json
// @Param body body MerchDTO true "новый мерч"
// @Success 200
// @Failure 400 {object} responses.ErrorResponse400
// @Failure 500 {object} responses.ErrorResponse500
// @Router /merch [post]
func (co *controller) addMerch(c *gin.Context) {
var payload MerchDTO
if err := c.ShouldBind(&payload); err != nil {
c.JSON(http.StatusBadRequest, responses.ErrorResponse400{Error: err.Error()})
log.WithError(err).Error("Merch | Failed to bind JSON on add merch")
return
}
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
}
err = co.service.addMerch(payload, userUuid)
if err != nil {
c.JSON(http.StatusBadRequest, responses.ErrorResponse500{Error: err.Error()})
log.WithError(err).Error("Merch | Failed to add merch")
return
}
c.Status(http.StatusOK)
}
// @Summary Получить всю информацию про мерч
// @Description Получить всю информацию про мерч по его uuid
// @Tags Merch
// @Security BearerAuth
// @Param uuid path string true "merch_uuid"
// @Success 200 {object} MerchDTO
// @Failure 400 {object} responses.ErrorResponse400
// @Failure 500 {object} responses.ErrorResponse500
// @Router /merch/{uuid} [get]
func (co *controller) getSingleMerch(c *gin.Context) {
merchUuid := c.Param("uuid")
if merchUuid == "" {
c.JSON(http.StatusBadRequest, responses.ErrorResponse400{Error: "MerchUuid is empty"})
return
}
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.getSingleMerch(userUuid, merchUuid)
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)
}
// @Summary Получить все записи мерча
// @Description Получить все записи мерча
// @Tags Merch
// @Security BearerAuth
// @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)
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.getAllMerch(userUuid)
if err != nil {
c.JSON(http.StatusBadRequest, responses.ErrorResponse500{Error: err.Error()})
log.WithError(err).Error("Merch | Failed to get all merch")
return
}
c.JSON(http.StatusOK, response)
}
// @Summary Обновить информацию про мерч
// @Description Обновить информацию про мерч по его uuid в json-е
// @Tags Merch
// @Security BearerAuth
// @Param body body UpdateMerchDTO true "merch_uuid"
// @Success 200
// @Failure 400 {object} responses.ErrorResponse400
// @Failure 500 {object} responses.ErrorResponse500
// @Router /merch/ [put]
func (co *controller) updateMerch(c *gin.Context) {
var payload UpdateMerchDTO
if err := c.ShouldBind(&payload); err != nil {
c.JSON(http.StatusBadRequest, responses.ErrorResponse400{Error: err.Error()})
log.WithError(err).Error("Merch | Failed to bind JSON on update merch")
return
}
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
}
if err = co.service.updateMerch(payload, userUuid); err != nil {
c.JSON(http.StatusBadRequest, responses.ErrorResponse500{Error: err.Error()})
log.WithError(err).Error("Merch | Failed to get single merch")
return
}
}
// @Summary Пометить мерч как удаленный
// @Description Пометить мерч как удаленный по его uuid
// @Tags Merch
// @Security BearerAuth
// @Param uuid path string true "merch_uuid"
// @Success 200 {object} MerchDTO
// @Failure 400 {object} responses.ErrorResponse400
// @Failure 500 {object} responses.ErrorResponse500
// @Router /merch/{uuid} [delete]
func (co *controller) deleteMerch(c *gin.Context) {
merchUuid := c.Param("uuid")
if merchUuid == "" {
c.JSON(http.StatusBadRequest, responses.ErrorResponse400{Error: "MerchUuid is empty"})
return
}
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
}
if err = co.service.deleteMerch(userUuid, merchUuid); err != nil {
c.JSON(http.StatusBadRequest, responses.ErrorResponse500{Error: err.Error()})
log.WithError(err).Error("Merch | Failed to get single merch")
return
}
c.Status(http.StatusOK)
}
// @Summary Получить цены мерча за период
// @Description Получить цены мерча за период
// @Tags Merch
// @Security BearerAuth
// @Param days query string false "period in days"
// @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)
}
// @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)
}
// @Summary Загрузить картинку по merch_uuid
// @Description Загрузить картинку по merch_uuid. В ответ будут выданы ссылки на созданные картинки.
// @Tags Merch images
// @Security BearerAuth
// @Accept multipart/form-data
// @Produce json
// @Param uuid path string true "Merch UUID"
// @Param file formData file true "Image file"
// @Success 200 {object} imageStorage.UploadMerchImageResponse
// @Failure 400 {object} responses.ErrorResponse400
// @Failure 500 {object} responses.ErrorResponse500
// @Router /merch/images/{uuid} [post]
func (co *controller) uploadMerchImage(c *gin.Context) {
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"})
log.Error("Merch | Failed to get single merch")
return
}
//Uncomment for MinIO use
//imageType := c.PostForm("imageType")
//types := map[string]struct{}{"thumbnail": {}, "full": {}, "all": {}}
//if _, allowed := types[imageType]; !allowed {
// c.JSON(http.StatusBadRequest, responses.ErrorResponse400{Error: "imageType must be one of: thumbnail, full, all"})
// log.WithError(err).Error("Merch | imageType must be one of: thumbnail, full, all")
// return
//}
file, err := c.FormFile("file")
if err != nil {
c.JSON(http.StatusBadRequest, responses.ErrorResponse400{Error: "file is required"})
log.WithError(err).Error("Merch | File is required")
return
}
ctx, cancel := context.WithTimeout(c.Request.Context(), co.expires)
defer cancel()
//Uncomment for MinIO use
//err = co.service.uploadMerchImage(ctx, userUuid, merchUuid, imageType, file)
response, err := co.service.mtUploadMerchImage(ctx, userUuid, merchUuid, file)
if err != nil {
c.JSON(http.StatusInternalServerError, responses.ErrorResponse500{Error: err.Error()})
log.WithError(err).Error("Merch | Failed to upload merch image")
return
}
//c.Status(http.StatusOK)
c.JSON(http.StatusOK, response)
}
// @Summary Получить картинки по merch_uuid и query параметрам
// @Description Получить картинки по merch_uuid и query параметрам
// @Tags Merch images
// @Security BearerAuth
// @Param uuid path string true "merch_uuid"
// @Param type query string true "image type"
// @Success 200 {object} ImageLink
// @Failure 400 {object} responses.ErrorResponse400
// @Failure 500 {object} responses.ErrorResponse500
// @Router /merch/images/{uuid} [get]
func (co *controller) getMerchImage(c *gin.Context) {
//Uncomment for MinIO use
//typeQuery := strings.ToLower(c.Query("type"))
//if typeQuery == "" {
// c.JSON(http.StatusBadRequest, responses.ErrorResponse400{Error: "Image type query param is empty"})
// return
//}
//
//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"})
// log.WithError(err).Error("Merch | Failed to get single merch")
// return
//}
//
//ctx, cancel := context.WithTimeout(c.Request.Context(), co.expires)
//defer cancel()
//
//link, err := co.service.getPublicImageLink(ctx, userUuid, merchUuid, typeQuery)
//if err != nil {
// c.JSON(http.StatusInternalServerError, responses.ErrorResponse500{Error: err.Error()})
// log.WithError(err).Error("Merch | Failed to get merch image")
// return
//}
//
//if link.Link == "" {
// log.Debug("Merch | No image")
// c.Status(http.StatusNoContent)
// return
//}
//
//c.JSON(http.StatusOK, link)
c.JSON(http.StatusNotImplemented, gin.H{"msg": "Method deprecated. Request images from image storage."})
}
// @Summary Удалить (безвозвратно) картинки по merch_uuid
// @Description Удалить (безвозвратно) картинки по merch_uuid
// @Tags Merch images
// @Security BearerAuth
// @Param uuid path string true "merch_uuid"
// @Success 200
// @Failure 400 {object} responses.ErrorResponse400
// @Failure 500 {object} responses.ErrorResponse500
// @Router /merch/images/{uuid} [delete]
func (co *controller) deleteMerchImage(c *gin.Context) {
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"})
log.WithError(err).Error("Merch | Failed to get merch uuid")
return
}
ctx, cancel := context.WithTimeout(c.Request.Context(), co.expires)
defer cancel()
//Uncomment for MinIO use
//if err := co.service.deleteMerchImage(ctx, userUuid, merchUuid); err != nil {
if err := co.service.mtDeleteMerchImage(ctx, userUuid, merchUuid); err != nil {
c.JSON(http.StatusInternalServerError, responses.ErrorResponse500{Error: err.Error()})
log.WithError(err).Error("Merch | Failed to delete merch image")
return
}
c.Status(http.StatusOK)
}
// @Summary Создать новую метку для товара
// @Description Создать новую метку для товара
// @Tags Merch labels
// @Security BearerAuth
// @Param payload body LabelDTO true "payload"
// @Success 200
// @Failure 400 {object} responses.ErrorResponse400
// @Failure 500 {object} responses.ErrorResponse500
// @Router /merch/labels [post]
func (co *controller) createLabel(c *gin.Context) {
const logMsg = "Merch | Create label"
userUuid, err := co.utils.GetUserUuidFromContext(c)
if err != nil {
c.JSON(http.StatusInternalServerError, responses.ErrorResponse500{Error: err.Error()})
log.WithError(err).Error(logMsg)
return
}
var payload LabelDTO
if err = c.ShouldBindJSON(&payload); err != nil {
c.JSON(http.StatusBadRequest, responses.ErrorResponse400{Error: err.Error()})
log.WithError(err).Error(logMsg)
return
}
if err = co.service.createLabel(payload, userUuid); err != nil {
c.JSON(http.StatusInternalServerError, responses.ErrorResponse500{Error: err.Error()})
log.WithError(err).Error(logMsg)
return
}
c.Status(http.StatusOK)
}
// @Summary Получить все метки товаров
// @Description Получить все метки товаров
// @Tags Merch labels
// @Security BearerAuth
// @Success 200 {array} LabelsList
// @Failure 400 {object} responses.ErrorResponse400
// @Failure 500 {object} responses.ErrorResponse500
// @Router /merch/labels [get]
func (co *controller) getLabels(c *gin.Context) {
const logMsg = "Merch | Get labels"
userUuid, err := co.utils.GetUserUuidFromContext(c)
if err != nil {
c.JSON(http.StatusInternalServerError, responses.ErrorResponse500{Error: err.Error()})
log.WithError(err).Error(logMsg)
return
}
response, err := co.service.getLabels(userUuid)
if err != nil {
c.JSON(http.StatusInternalServerError, responses.ErrorResponse500{Error: err.Error()})
log.WithError(err).Error(logMsg)
return
}
c.JSON(http.StatusOK, response)
}
// @Summary Изменить метку
// @Description Изменить метку
// @Tags Merch labels
// @Security BearerAuth
// @Param uuid path string true "label uuid"
// @Param payload body LabelDTO true "payload"
// @Success 200
// @Failure 400 {object} responses.ErrorResponse400
// @Failure 500 {object} responses.ErrorResponse500
// @Router /merch/labels/{uuid} [put]
func (co *controller) updateLabel(c *gin.Context) {
const logMsg = "Merch | Update label"
userUuid, err := co.utils.GetUserUuidFromContext(c)
if err != nil {
c.JSON(http.StatusInternalServerError, responses.ErrorResponse500{Error: err.Error()})
log.WithError(err).Error(logMsg)
return
}
labelUuid := c.Param("uuid")
if labelUuid == "" {
c.JSON(http.StatusBadRequest, responses.ErrorResponse400{Error: "label uuid is empty"})
log.WithError(err).Error(logMsg)
return
}
var payload LabelDTO
if err = c.ShouldBindJSON(&payload); err != nil {
c.JSON(http.StatusBadRequest, responses.ErrorResponse400{Error: err.Error()})
log.WithError(err).Error(logMsg)
return
}
if err = co.service.updateLabel(userUuid, labelUuid, payload); err != nil {
c.JSON(http.StatusInternalServerError, responses.ErrorResponse500{Error: err.Error()})
log.WithError(err).Error(logMsg)
return
}
c.Status(http.StatusOK)
}
// @Summary Пометить метку как удаленную
// @Description Пометить метку как удаленную
// @Tags Merch labels
// @Security BearerAuth
// @Param uuid path string true "label uuid"
// @Success 200
// @Failure 400 {object} responses.ErrorResponse400
// @Failure 500 {object} responses.ErrorResponse500
// @Router /merch/labels/{uuid} [delete]
func (co *controller) deleteLabel(c *gin.Context) {
const logMsg = "Merch | Delete label"
userUuid, err := co.utils.GetUserUuidFromContext(c)
if err != nil {
c.JSON(http.StatusInternalServerError, responses.ErrorResponse500{Error: err.Error()})
log.WithError(err).Error(logMsg)
return
}
labelUuid := c.Param("uuid")
if labelUuid == "" {
c.JSON(http.StatusBadRequest, responses.ErrorResponse400{Error: "label uuid is empty"})
log.WithError(err).Error(logMsg)
return
}
if err = co.service.deleteLabel(userUuid, labelUuid); err != nil {
c.JSON(http.StatusInternalServerError, responses.ErrorResponse500{Error: err.Error()})
log.WithError(err).Error(logMsg)
return
}
c.Status(http.StatusOK)
}
// @Summary Прикрепить метку к товару
// @Description Прикрепить метку к товару
// @Tags Merch labels
// @Security BearerAuth
// @Param payload body LabelLink true "payload"
// @Success 200
// @Failure 400 {object} responses.ErrorResponse400
// @Failure 500 {object} responses.ErrorResponse500
// @Router /merch/labels/attach [post]
func (co *controller) attachLabel(c *gin.Context) {
const logMsg = "Merch | Attach label"
userUuid, err := co.utils.GetUserUuidFromContext(c)
if err != nil {
c.JSON(http.StatusInternalServerError, responses.ErrorResponse500{Error: err.Error()})
log.WithError(err).Error(logMsg)
return
}
var payload LabelLink
if err = c.ShouldBindJSON(&payload); err != nil {
c.JSON(http.StatusBadRequest, responses.ErrorResponse400{Error: err.Error()})
log.WithError(err).Error(logMsg)
return
}
if err = co.service.attachLabel(userUuid, payload); err != nil {
c.JSON(http.StatusInternalServerError, responses.ErrorResponse500{Error: err.Error()})
log.WithError(err).Error(logMsg)
return
}
c.Status(http.StatusOK)
}
// @Summary Удалить привязку метки к товару
// @Description Удалить привязку метки к товару
// @Tags Merch labels
// @Security BearerAuth
// @Param payload body LabelLink true "payload"
// @Success 200
// @Failure 400 {object} responses.ErrorResponse400
// @Failure 500 {object} responses.ErrorResponse500
// @Router /merch/labels/detach [post]
func (co *controller) detachLabel(c *gin.Context) {
const logMsg = "Merch | Detach label"
userUuid, err := co.utils.GetUserUuidFromContext(c)
if err != nil {
c.JSON(http.StatusInternalServerError, responses.ErrorResponse500{Error: err.Error()})
log.WithError(err).Error(logMsg)
return
}
var payload LabelLink
if err = c.ShouldBindJSON(&payload); err != nil {
c.JSON(http.StatusBadRequest, responses.ErrorResponse400{Error: err.Error()})
log.WithError(err).Error(logMsg)
return
}
if err = co.service.detachLabel(userUuid, payload); err != nil {
c.JSON(http.StatusInternalServerError, responses.ErrorResponse500{Error: err.Error()})
log.WithError(err).Error(logMsg)
return
}
c.Status(http.StatusOK)
}