Merge branch 'promoList' into 'dev'
Promo list See merge request pena-services/codeword!8
This commit is contained in:
commit
fd8c986e22
@ -165,6 +165,34 @@ paths:
|
|||||||
error:
|
error:
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
|
/promocode/getList:
|
||||||
|
post:
|
||||||
|
summary: Получить список промокодов
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GetPromoCodesListReq'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Список промокодов и общее количество успешно получены
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GetPromoCodesListResp'
|
||||||
|
'400':
|
||||||
|
description: Неверный запрос из-за невалидных данных
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
|
'500':
|
||||||
|
description: Внутренняя ошибка сервера
|
||||||
|
|
||||||
|
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
@ -298,3 +326,40 @@ components:
|
|||||||
description: Флаг удаления промокода
|
description: Флаг удаления промокода
|
||||||
required:
|
required:
|
||||||
- id
|
- id
|
||||||
|
GetPromoCodesListReq:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- page
|
||||||
|
- limit
|
||||||
|
- filter
|
||||||
|
properties:
|
||||||
|
page:
|
||||||
|
type: integer
|
||||||
|
description: Номер страницы выборки, начиная с 0
|
||||||
|
limit:
|
||||||
|
type: integer
|
||||||
|
description: Размер страницы выборки
|
||||||
|
filter:
|
||||||
|
$ref: '#/components/schemas/GetPromoCodesListReqFilter'
|
||||||
|
|
||||||
|
GetPromoCodesListReqFilter:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
text:
|
||||||
|
type: string
|
||||||
|
description: Полнотекстовый поиск по полям Codeword, Description, Greetings
|
||||||
|
active:
|
||||||
|
type: boolean
|
||||||
|
description: Если true, выбираются записи, где delete, outdated и offLimit равны false
|
||||||
|
|
||||||
|
GetPromoCodesListResp:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
count:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
description: Общее количество промокодов в выборке
|
||||||
|
items:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/PromoCodeResponse'
|
@ -22,12 +22,12 @@ func NewPromoCodeController(logger *zap.Logger, promoCodeService *services.Promo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *PromoCodeController) CreatePromoCode(c *fiber.Ctx) error {
|
func (p *PromoCodeController) CreatePromoCode(c *fiber.Ctx) error {
|
||||||
var reqCreatePromoCode models.PromoCode
|
var req models.PromoCode
|
||||||
if err := c.BodyParser(&reqCreatePromoCode); err != nil {
|
if err := c.BodyParser(&req); err != nil {
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||||
}
|
}
|
||||||
|
|
||||||
createdPromoCode, err := p.promoCodeService.CreatePromoCode(c.Context(), &reqCreatePromoCode)
|
createdPromoCode, err := p.promoCodeService.CreatePromoCode(c.Context(), &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.logger.Error("Failed to create promocode", zap.Error(err))
|
p.logger.Error("Failed to create promocode", zap.Error(err))
|
||||||
|
|
||||||
@ -42,16 +42,16 @@ func (p *PromoCodeController) CreatePromoCode(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *PromoCodeController) EditPromoCode(c *fiber.Ctx) error {
|
func (p *PromoCodeController) EditPromoCode(c *fiber.Ctx) error {
|
||||||
var reqEditPromoCode models.ReqEditPromoCode
|
var req models.ReqEditPromoCode
|
||||||
if err := c.BodyParser(&reqEditPromoCode); err != nil {
|
if err := c.BodyParser(&req); err != nil {
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||||
}
|
}
|
||||||
|
|
||||||
if reqEditPromoCode.ID == "" {
|
if req.ID == "" {
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "promocode ID is required"})
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "promocode ID is required"})
|
||||||
}
|
}
|
||||||
|
|
||||||
editedPromoCode, err := p.promoCodeService.EditPromoCode(c.Context(), &reqEditPromoCode)
|
editedPromoCode, err := p.promoCodeService.EditPromoCode(c.Context(), &req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.logger.Error("Failed to edit promocode", zap.Error(err))
|
p.logger.Error("Failed to edit promocode", zap.Error(err))
|
||||||
|
|
||||||
@ -64,3 +64,22 @@ func (p *PromoCodeController) EditPromoCode(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
return c.Status(fiber.StatusOK).JSON(editedPromoCode)
|
return c.Status(fiber.StatusOK).JSON(editedPromoCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PromoCodeController) GetList(c *fiber.Ctx) error {
|
||||||
|
var req models.GetPromoCodesListReq
|
||||||
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||||
|
}
|
||||||
|
|
||||||
|
promoCodes, count, err := p.promoCodeService.GetPromoCodesList(c.Context(), &req)
|
||||||
|
if err != nil {
|
||||||
|
p.logger.Error("Failed to retrieve promocode list", zap.Error(err))
|
||||||
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := models.GetPromoCodesListResp{
|
||||||
|
Count: count,
|
||||||
|
Items: promoCodes,
|
||||||
|
}
|
||||||
|
return c.Status(fiber.StatusOK).JSON(resp)
|
||||||
|
}
|
||||||
|
@ -40,3 +40,19 @@ type ReqEditPromoCode struct {
|
|||||||
|
|
||||||
Delete *bool `json:"delete,omitempty" bson:"delete"`
|
Delete *bool `json:"delete,omitempty" bson:"delete"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetPromoCodesListReqFilter struct {
|
||||||
|
Text string `json:"text"` // полнотекстовый поиск пo Codeword, Decription, Greetings полям
|
||||||
|
Active bool `json:"active"` // если true, то выбирать deleted==false && outdated== false && offlimit == false
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetPromoCodesListReq struct {
|
||||||
|
Page int `json:"page"` //номер страницы выборки. начинается с 0. по сути, skip для выборки из mongodb
|
||||||
|
Limit int `json:"limit"` //размер страницы выборки. больше 10, меньше 250. отвечает за skip = page*limit, и за limit
|
||||||
|
Filter GetPromoCodesListReqFilter `json:"filter"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetPromoCodesListResp struct {
|
||||||
|
Count int64 `json:"count"` // количество в выборке всего
|
||||||
|
Items []PromoCode `json:"items"` // "страница" промокодов
|
||||||
|
}
|
||||||
|
@ -16,19 +16,39 @@ var (
|
|||||||
ErrPromoCodeNotFound = errors.New("promo code not found")
|
ErrPromoCodeNotFound = errors.New("promo code not found")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// структура для горутины чтобы ошибки не пропускать
|
||||||
|
type countResult struct {
|
||||||
|
count int64
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
type PromoCodeRepository struct {
|
type PromoCodeRepository struct {
|
||||||
mdb *mongo.Collection
|
mdb *mongo.Collection
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPromoCodeRepository(mdb *mongo.Collection) *PromoCodeRepository {
|
func NewPromoCodeRepository(mdb *mongo.Collection) *PromoCodeRepository {
|
||||||
indexModel := mongo.IndexModel{
|
// todo заменить паники вроде как в роде не круто их юзать
|
||||||
|
uniqueIndexModel := mongo.IndexModel{
|
||||||
Keys: bson.D{
|
Keys: bson.D{
|
||||||
{"codeword", 1},
|
{Key: "codeword", Value: 1},
|
||||||
{"delete", 1},
|
{Key: "delete", Value: 1},
|
||||||
},
|
},
|
||||||
Options: options.Index().SetUnique(true).SetPartialFilterExpression(bson.M{"delete": false}),
|
Options: options.Index().SetUnique(true).SetPartialFilterExpression(bson.M{"delete": false}),
|
||||||
}
|
}
|
||||||
_, err := mdb.Indexes().CreateOne(context.Background(), indexModel)
|
_, err := mdb.Indexes().CreateOne(context.Background(), uniqueIndexModel)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
textIndexModel := mongo.IndexModel{
|
||||||
|
Keys: bson.D{
|
||||||
|
{Key: "codeword", Value: "text"},
|
||||||
|
{Key: "description", Value: "text"},
|
||||||
|
{Key: "greetings", Value: "text"},
|
||||||
|
},
|
||||||
|
Options: options.Index().SetName("TextIndex"),
|
||||||
|
}
|
||||||
|
_, err = mdb.Indexes().CreateOne(context.Background(), textIndexModel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -36,11 +56,11 @@ func NewPromoCodeRepository(mdb *mongo.Collection) *PromoCodeRepository {
|
|||||||
return &PromoCodeRepository{mdb: mdb}
|
return &PromoCodeRepository{mdb: mdb}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *PromoCodeRepository) CreatePromoCode(ctx context.Context, promoCode *models.PromoCode) (*models.PromoCode, error) {
|
func (r *PromoCodeRepository) CreatePromoCode(ctx context.Context, req *models.PromoCode) (*models.PromoCode, error) {
|
||||||
promoCode.CreatedAt = time.Now()
|
req.CreatedAt = time.Now()
|
||||||
promoCode.ID = primitive.NewObjectID()
|
req.ID = primitive.NewObjectID()
|
||||||
|
|
||||||
_, err := r.mdb.InsertOne(ctx, promoCode)
|
_, err := r.mdb.InsertOne(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if writeErr, ok := err.(mongo.WriteException); ok {
|
if writeErr, ok := err.(mongo.WriteException); ok {
|
||||||
for _, writeError := range writeErr.WriteErrors {
|
for _, writeError := range writeErr.WriteErrors {
|
||||||
@ -53,30 +73,30 @@ func (r *PromoCodeRepository) CreatePromoCode(ctx context.Context, promoCode *mo
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return promoCode, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *PromoCodeRepository) EditPromoCode(ctx context.Context, editPromoCode *models.ReqEditPromoCode) (*models.PromoCode, error) {
|
func (r *PromoCodeRepository) EditPromoCode(ctx context.Context, req *models.ReqEditPromoCode) (*models.PromoCode, error) {
|
||||||
promoCodeID, err := primitive.ObjectIDFromHex(editPromoCode.ID)
|
promoCodeID, err := primitive.ObjectIDFromHex(req.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFields := bson.M{}
|
updateFields := bson.M{}
|
||||||
if editPromoCode.Description != nil {
|
if req.Description != nil {
|
||||||
updateFields["description"] = *editPromoCode.Description
|
updateFields["description"] = *req.Description
|
||||||
}
|
}
|
||||||
if editPromoCode.Greetings != nil {
|
if req.Greetings != nil {
|
||||||
updateFields["greetings"] = *editPromoCode.Greetings
|
updateFields["greetings"] = *req.Greetings
|
||||||
}
|
}
|
||||||
if editPromoCode.DueTo != nil {
|
if req.DueTo != nil {
|
||||||
updateFields["dueTo"] = *editPromoCode.DueTo
|
updateFields["dueTo"] = *req.DueTo
|
||||||
}
|
}
|
||||||
if editPromoCode.ActivationCount != nil {
|
if req.ActivationCount != nil {
|
||||||
updateFields["activationCount"] = *editPromoCode.ActivationCount
|
updateFields["activationCount"] = *req.ActivationCount
|
||||||
}
|
}
|
||||||
if editPromoCode.Delete != nil {
|
if req.Delete != nil {
|
||||||
updateFields["delete"] = *editPromoCode.Delete
|
updateFields["delete"] = *req.Delete
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(updateFields) == 0 {
|
if len(updateFields) == 0 {
|
||||||
@ -109,3 +129,59 @@ func (r *PromoCodeRepository) GetPromoCodeByID(ctx context.Context, promoCodeID
|
|||||||
|
|
||||||
return &promoCode, nil
|
return &promoCode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *PromoCodeRepository) GetPromoCodesList(ctx context.Context, req *models.GetPromoCodesListReq) ([]models.PromoCode, int64, error) {
|
||||||
|
filter := bson.M{}
|
||||||
|
|
||||||
|
if req.Filter.Text != "" {
|
||||||
|
filter["$text"] = bson.M{"$search": req.Filter.Text}
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Filter.Active {
|
||||||
|
filter["delete"] = false
|
||||||
|
filter["outdated"] = false
|
||||||
|
filter["offLimit"] = false
|
||||||
|
} else {
|
||||||
|
filter["$or"] = []interface{}{
|
||||||
|
bson.M{"delete": true},
|
||||||
|
bson.M{"outdated": true},
|
||||||
|
bson.M{"offLimit": true},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opt := options.Find().SetSkip(int64(req.Page * req.Limit)).SetLimit(int64(req.Limit))
|
||||||
|
|
||||||
|
var countChan = make(chan countResult)
|
||||||
|
go func() {
|
||||||
|
defer close(countChan)
|
||||||
|
count, err := r.mdb.CountDocuments(ctx, filter)
|
||||||
|
countChan <- countResult{count, err}
|
||||||
|
}()
|
||||||
|
|
||||||
|
cursor, err := r.mdb.Find(ctx, filter, opt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
defer cursor.Close(ctx)
|
||||||
|
|
||||||
|
var promoCodes = make([]models.PromoCode, 0)
|
||||||
|
for cursor.Next(ctx) {
|
||||||
|
var p models.PromoCode
|
||||||
|
if err := cursor.Decode(&p); err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
promoCodes = append(promoCodes, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cursor.Err(); err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := <-countChan
|
||||||
|
if result.err != nil {
|
||||||
|
return nil, 0, result.err
|
||||||
|
}
|
||||||
|
count := result.count
|
||||||
|
|
||||||
|
return promoCodes, count, nil
|
||||||
|
}
|
||||||
|
@ -59,6 +59,7 @@ func (s *Server) registerRoutes() {
|
|||||||
|
|
||||||
s.app.Post("/promocode/create", s.PromoCodeController.CreatePromoCode)
|
s.app.Post("/promocode/create", s.PromoCodeController.CreatePromoCode)
|
||||||
s.app.Put("/promocode/edit", s.PromoCodeController.EditPromoCode)
|
s.app.Put("/promocode/edit", s.PromoCodeController.EditPromoCode)
|
||||||
|
s.app.Post("/promocode/getList", s.PromoCodeController.GetList)
|
||||||
//... other
|
//... other
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,8 +7,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type PromoCodeRepository interface {
|
type PromoCodeRepository interface {
|
||||||
CreatePromoCode(ctx context.Context, promoCode *models.PromoCode) (*models.PromoCode, error)
|
CreatePromoCode(ctx context.Context, req *models.PromoCode) (*models.PromoCode, error)
|
||||||
EditPromoCode(ctx context.Context, reqEditPromoCode *models.ReqEditPromoCode) (*models.PromoCode, error)
|
EditPromoCode(ctx context.Context, req *models.ReqEditPromoCode) (*models.PromoCode, error)
|
||||||
|
GetPromoCodesList(ctx context.Context, req *models.GetPromoCodesListReq) ([]models.PromoCode, int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type PromoDeps struct {
|
type PromoDeps struct {
|
||||||
@ -47,3 +48,13 @@ func (s *PromoCodeService) EditPromoCode(ctx context.Context, req *models.ReqEdi
|
|||||||
|
|
||||||
return editedPromoCode, nil
|
return editedPromoCode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *PromoCodeService) GetPromoCodesList(ctx context.Context, req *models.GetPromoCodesListReq) ([]models.PromoCode, int64, error) {
|
||||||
|
promoCodes, count, err := s.promoCodeRepo.GetPromoCodesList(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to get list promocodes from database", zap.Error(err))
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return promoCodes, count, nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user