From 483eb2772365879c6e29cf0874ebee704fb5d427 Mon Sep 17 00:00:00 2001 From: Pavel Date: Fri, 12 Jan 2024 16:46:36 +0300 Subject: [PATCH 1/3] add base logic for activate promo --- .../promocode/promocode_controller.go | 27 ++++++++++++ internal/models/bonus.go | 8 ++++ internal/repository/promocode_repository.go | 44 +++++++++++++++++++ internal/server/http/http_server.go | 1 + internal/services/promocode_service.go | 11 +++++ 5 files changed, 91 insertions(+) diff --git a/internal/controller/promocode/promocode_controller.go b/internal/controller/promocode/promocode_controller.go index 783d4ea..75fe521 100644 --- a/internal/controller/promocode/promocode_controller.go +++ b/internal/controller/promocode/promocode_controller.go @@ -83,3 +83,30 @@ func (p *PromoCodeController) GetList(c *fiber.Ctx) error { } return c.Status(fiber.StatusOK).JSON(resp) } + +func (p *PromoCodeController) Activate(c *fiber.Ctx) error { + var req models.ActivateReq + if err := c.BodyParser(&req); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"}) + } + + if req.Codeword == "" { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "codeword is required"}) + } + + greetings, err := p.promoCodeService.ActivatePromo(c.Context(), &req) + if err != nil { + p.logger.Error("Failed to activate promocode", zap.Error(err)) + + if errors.Is(err, repository.ErrPromoCodeNotFound) { + return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "PromoCode not found"}) + } + + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"}) + } + + resp := models.ActivateResp{ + Greetings: greetings, + } + return c.Status(fiber.StatusOK).JSON(resp) +} diff --git a/internal/models/bonus.go b/internal/models/bonus.go index 7a4dc9e..4bec834 100644 --- a/internal/models/bonus.go +++ b/internal/models/bonus.go @@ -56,3 +56,11 @@ type GetPromoCodesListResp struct { Count int64 `json:"count"` // количество в выборке всего Items []PromoCode `json:"items"` // "страница" промокодов } + +type ActivateReq struct { + Codeword string `json:"codeword"` +} + +type ActivateResp struct { + Greetings string `json:"greetings"` // поле из активированного промокода +} diff --git a/internal/repository/promocode_repository.go b/internal/repository/promocode_repository.go index 83944ef..1e6fab5 100644 --- a/internal/repository/promocode_repository.go +++ b/internal/repository/promocode_repository.go @@ -185,3 +185,47 @@ func (r *PromoCodeRepository) GetPromoCodesList(ctx context.Context, req *models return promoCodes, count, nil } + +func (r *PromoCodeRepository) ActivatePromo(ctx context.Context, req *models.ActivateReq) (string, error) { + session, err := r.mdb.Database().Client().StartSession() + if err != nil { + return "", err + } + defer session.EndSession(ctx) + + var greetings string + + transactionErr := mongo.WithSession(ctx, session, func(sc mongo.SessionContext) error { + filter := bson.M{ + "codeword": req.Codeword, + "delete": false, + "outdated": false, + "offLimit": false, + "activationCount": bson.M{"$gt": 0}, + "dueTo": bson.M{"$gt": time.Now().Unix()}, + } + update := bson.M{ + "$inc": bson.M{"activationCount": -1}, + } + opts := options.FindOneAndUpdate().SetReturnDocument(options.After) + + var updatedPromoCode models.PromoCode + err := r.mdb.FindOneAndUpdate(sc, filter, update, opts).Decode(&updatedPromoCode) + if err != nil { + if err == mongo.ErrNoDocuments { + return ErrPromoCodeNotFound + } + return err + } + + greetings = updatedPromoCode.Greetings + + return nil + }) + + if transactionErr != nil { + return "", transactionErr + } + + return greetings, nil +} diff --git a/internal/server/http/http_server.go b/internal/server/http/http_server.go index 43e75cd..ce4a0c3 100644 --- a/internal/server/http/http_server.go +++ b/internal/server/http/http_server.go @@ -60,6 +60,7 @@ func (s *Server) registerRoutes() { s.app.Post("/promocode/create", s.PromoCodeController.CreatePromoCode) s.app.Put("/promocode/edit", s.PromoCodeController.EditPromoCode) s.app.Post("/promocode/getList", s.PromoCodeController.GetList) + s.app.Post("/promocode/activate", s.PromoCodeController.Activate) //... other } diff --git a/internal/services/promocode_service.go b/internal/services/promocode_service.go index 11cd344..a016f4a 100644 --- a/internal/services/promocode_service.go +++ b/internal/services/promocode_service.go @@ -10,6 +10,7 @@ type PromoCodeRepository interface { CreatePromoCode(ctx context.Context, req *models.PromoCode) (*models.PromoCode, error) EditPromoCode(ctx context.Context, req *models.ReqEditPromoCode) (*models.PromoCode, error) GetPromoCodesList(ctx context.Context, req *models.GetPromoCodesListReq) ([]models.PromoCode, int64, error) + ActivatePromo(ctx context.Context, req *models.ActivateReq) (string, error) } type PromoDeps struct { @@ -58,3 +59,13 @@ func (s *PromoCodeService) GetPromoCodesList(ctx context.Context, req *models.Ge return promoCodes, count, nil } + +func (s *PromoCodeService) ActivatePromo(ctx context.Context, req *models.ActivateReq) (string, error) { + greetings, err := s.promoCodeRepo.ActivatePromo(ctx, req) + if err != nil { + s.logger.Error("Failed to activate promocode", zap.Error(err)) + return "", err + } + + return greetings, nil +} From 45e833a74b840b4913b0188ed7d0ea964eaf8b02 Mon Sep 17 00:00:00 2001 From: Pavel Date: Fri, 12 Jan 2024 16:57:51 +0300 Subject: [PATCH 2/3] add some docs --- docs/openapi.yaml | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 4c5383f..48a4d6a 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -193,6 +193,29 @@ paths: '500': description: Внутренняя ошибка сервера + /promocode/activate: + post: + summary: Активировать промокод + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ActivateReq' + responses: + '200': + description: Промокод успешно активирован + content: + application/json: + schema: + $ref: '#/components/schemas/ActivateResp' + '400': + description: Невалидный запрос или отсутствует обязательное поле codeword + '404': + description: Промокод не найден + '500': + description: Внутренняя ошибка сервера + components: schemas: @@ -368,4 +391,20 @@ components: items: type: array items: - $ref: '#/components/schemas/PromoCodeResponse' \ No newline at end of file + $ref: '#/components/schemas/PromoCodeResponse' + + ActivateReq: + type: object + required: + - codeword + properties: + codeword: + type: string + description: Кодовое слово промокода, которое требуется активировать + + ActivateResp: + type: object + properties: + greetings: + type: string + description: Поле из активированного промокода \ No newline at end of file From 1019fd3f831e86fd7692f211544833b0caa46fb3 Mon Sep 17 00:00:00 2001 From: Pavel Date: Fri, 12 Jan 2024 17:50:53 +0300 Subject: [PATCH 3/3] add base logic for delete promocode --- docs/openapi.yaml | 41 +++++++++++++++++++ .../promocode/promocode_controller.go | 23 ++++++++++- internal/repository/promocode_repository.go | 22 ++++++++++ internal/server/http/http_server.go | 1 + internal/services/promocode_service.go | 11 +++++ 5 files changed, 97 insertions(+), 1 deletion(-) diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 48a4d6a..6604ac8 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -216,6 +216,47 @@ paths: '500': description: Внутренняя ошибка сервера + /promocode/{promocodeID}: + delete: + summary: Мягко удалить промокод по его id + parameters: + - in: path + name: promocodeID + required: true + schema: + type: string + description: Id промокода для удаления + responses: + '204': + description: Промокод успешно помечен как удаленный + '400': + description: Неверный запрос, отсутствует идентификатор промокода + content: + application/json: + schema: + type: object + properties: + error: + type: string + '404': + description: Промокод не найден + content: + application/json: + schema: + type: object + properties: + error: + type: string + '500': + description: Внутренняя ошибка сервера + content: + application/json: + schema: + type: object + properties: + error: + type: string + components: schemas: diff --git a/internal/controller/promocode/promocode_controller.go b/internal/controller/promocode/promocode_controller.go index 75fe521..7f0b358 100644 --- a/internal/controller/promocode/promocode_controller.go +++ b/internal/controller/promocode/promocode_controller.go @@ -101,7 +101,7 @@ func (p *PromoCodeController) Activate(c *fiber.Ctx) error { if errors.Is(err, repository.ErrPromoCodeNotFound) { return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "PromoCode not found"}) } - + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"}) } @@ -110,3 +110,24 @@ func (p *PromoCodeController) Activate(c *fiber.Ctx) error { } return c.Status(fiber.StatusOK).JSON(resp) } + +func (p *PromoCodeController) Delete(c *fiber.Ctx) error { + promoCodeID := c.Params("promocodeID") + + if promoCodeID == "" { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "PromoCode ID is required"}) + } + + err := p.promoCodeService.DeletePromoCode(c.Context(), promoCodeID) + if err != nil { + p.logger.Error("Failed to delete promocode", zap.Error(err)) + + if errors.Is(err, repository.ErrPromoCodeNotFound) { + return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "PromoCode not found"}) + } + + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"}) + } + + return c.SendStatus(fiber.StatusOK) +} diff --git a/internal/repository/promocode_repository.go b/internal/repository/promocode_repository.go index 1e6fab5..fbfb6d8 100644 --- a/internal/repository/promocode_repository.go +++ b/internal/repository/promocode_repository.go @@ -229,3 +229,25 @@ func (r *PromoCodeRepository) ActivatePromo(ctx context.Context, req *models.Act return greetings, nil } + +func (r *PromoCodeRepository) DeletePromoCode(ctx context.Context, promoCodeID string) error { + id, err := primitive.ObjectIDFromHex(promoCodeID) + if err != nil { + return err + } + + result, err := r.mdb.UpdateOne( + ctx, + bson.M{"_id": id, "delete": false}, + bson.M{"$set": bson.M{"delete": true}}, + ) + if err != nil { + return err + } + + if result.MatchedCount == 0 { + return ErrPromoCodeNotFound + } + + return nil +} diff --git a/internal/server/http/http_server.go b/internal/server/http/http_server.go index ce4a0c3..984ef7d 100644 --- a/internal/server/http/http_server.go +++ b/internal/server/http/http_server.go @@ -61,6 +61,7 @@ func (s *Server) registerRoutes() { s.app.Put("/promocode/edit", s.PromoCodeController.EditPromoCode) s.app.Post("/promocode/getList", s.PromoCodeController.GetList) s.app.Post("/promocode/activate", s.PromoCodeController.Activate) + s.app.Delete("/promocode/:promocodeID", s.PromoCodeController.Delete) //... other } diff --git a/internal/services/promocode_service.go b/internal/services/promocode_service.go index a016f4a..2197c85 100644 --- a/internal/services/promocode_service.go +++ b/internal/services/promocode_service.go @@ -11,6 +11,7 @@ type PromoCodeRepository interface { EditPromoCode(ctx context.Context, req *models.ReqEditPromoCode) (*models.PromoCode, error) GetPromoCodesList(ctx context.Context, req *models.GetPromoCodesListReq) ([]models.PromoCode, int64, error) ActivatePromo(ctx context.Context, req *models.ActivateReq) (string, error) + DeletePromoCode(ctx context.Context, promoCodeID string) error } type PromoDeps struct { @@ -69,3 +70,13 @@ func (s *PromoCodeService) ActivatePromo(ctx context.Context, req *models.Activa return greetings, nil } + +func (s *PromoCodeService) DeletePromoCode(ctx context.Context, promoCodeID string) error { + err := s.promoCodeRepo.DeletePromoCode(ctx, promoCodeID) + if err != nil { + s.logger.Error("Failed simple delete promocode from database", zap.Error(err)) + return err + } + + return nil +}