diff --git a/go.mod b/go.mod index 3dbfa38..f9eb5a8 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/rs/xid v1.5.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.50.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect diff --git a/go.sum b/go.sum index 05dc7a4..8e1373d 100644 --- a/go.sum +++ b/go.sum @@ -47,6 +47,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/internal/controller/promocode/promocode_controller.go b/internal/controller/promocode/promocode_controller.go index 7f0b358..fecee9b 100644 --- a/internal/controller/promocode/promocode_controller.go +++ b/internal/controller/promocode/promocode_controller.go @@ -84,14 +84,15 @@ func (p *PromoCodeController) GetList(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(resp) } +// todo затестить и обновить в opnapi 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"}) + if req.Codeword == "" && req.FastLink == "" { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "codeword or fastlink is required"}) } greetings, err := p.promoCodeService.ActivatePromo(c.Context(), &req) @@ -131,3 +132,27 @@ func (p *PromoCodeController) Delete(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusOK) } + +// todo затестить и добавить в opnapi +func (p *PromoCodeController) CreateFastLink(c *fiber.Ctx) error { + // нужно что-то получать из Localstorage например id или codeword + var req struct { + PromoCodeID string `json:"promoCodeID"` + } + if err := c.BodyParser(&req); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"}) + } + + fastLink, err := p.promoCodeService.CreateFastLink(c.Context(), req.PromoCodeID) + if err != nil { + p.logger.Error("Failed to create fastlink", 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.Status(fiber.StatusCreated).JSON(fiber.Map{"fastlink": fastLink}) +} diff --git a/internal/models/bonus.go b/internal/models/bonus.go index 4bec834..f6edeb4 100644 --- a/internal/models/bonus.go +++ b/internal/models/bonus.go @@ -28,6 +28,7 @@ type PromoCode struct { OffLimit bool `json:"offLimit" bson:"offLimit"` Delete bool `json:"delete" bson:"delete"` CreatedAt time.Time `json:"createdAt" bson:"createdAt"` + FastLinks []string `json:"fastLinks" bson:"fastLinks"` } type ReqEditPromoCode struct { @@ -59,6 +60,7 @@ type GetPromoCodesListResp struct { type ActivateReq struct { Codeword string `json:"codeword"` + FastLink string `json:"fastLink"` } type ActivateResp struct { diff --git a/internal/repository/promocode_repository.go b/internal/repository/promocode_repository.go index fbfb6d8..69984eb 100644 --- a/internal/repository/promocode_repository.go +++ b/internal/repository/promocode_repository.go @@ -196,14 +196,30 @@ func (r *PromoCodeRepository) ActivatePromo(ctx context.Context, req *models.Act 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()}, + var filter bson.M + + if req.Codeword != "" { + filter = bson.M{ + "codeword": req.Codeword, + "delete": false, + "outdated": false, + "offLimit": false, + "activationCount": bson.M{"$gt": 0}, + "dueTo": bson.M{"$gt": time.Now().Unix()}, + } + } else if req.FastLink != "" { + filter = bson.M{ + "fastLinks": req.FastLink, + "delete": false, + "outdated": false, + "offLimit": false, + "activationCount": bson.M{"$gt": 0}, + "dueTo": bson.M{"$gt": time.Now().Unix()}, + } + } else { + return errors.New("codeword or xid is required") } + update := bson.M{ "$inc": bson.M{"activationCount": -1}, } @@ -251,3 +267,19 @@ func (r *PromoCodeRepository) DeletePromoCode(ctx context.Context, promoCodeID s return nil } + +func (r *PromoCodeRepository) AddFastLink(ctx context.Context, promoCodeID primitive.ObjectID, xid string) error { + filter := bson.M{"_id": promoCodeID, "delete": false} + update := bson.M{"$push": bson.M{"fastLinks": xid}} + + result, err := r.mdb.UpdateOne(ctx, filter, update) + 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 984ef7d..b88d824 100644 --- a/internal/server/http/http_server.go +++ b/internal/server/http/http_server.go @@ -62,6 +62,7 @@ func (s *Server) registerRoutes() { s.app.Post("/promocode/getList", s.PromoCodeController.GetList) s.app.Post("/promocode/activate", s.PromoCodeController.Activate) s.app.Delete("/promocode/:promocodeID", s.PromoCodeController.Delete) + s.app.Post("/promocode/fastlink", s.PromoCodeController.CreateFastLink) //... other } diff --git a/internal/services/promocode_service.go b/internal/services/promocode_service.go index 2197c85..cbdd179 100644 --- a/internal/services/promocode_service.go +++ b/internal/services/promocode_service.go @@ -2,7 +2,9 @@ package services import ( "codeword/internal/models" + "codeword/internal/utils/genID" "context" + "go.mongodb.org/mongo-driver/bson/primitive" "go.uber.org/zap" ) @@ -12,6 +14,8 @@ type PromoCodeRepository interface { 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 + GetPromoCodeByID(ctx context.Context, promoCodeID primitive.ObjectID) (*models.PromoCode, error) + AddFastLink(ctx context.Context, promoCodeID primitive.ObjectID, xid string) error } type PromoDeps struct { @@ -80,3 +84,20 @@ func (s *PromoCodeService) DeletePromoCode(ctx context.Context, promoCodeID stri return nil } + +func (s *PromoCodeService) CreateFastLink(ctx context.Context, promoCodeID string) (string, error) { + xid := genID.GenerateXID() + promoID, err := primitive.ObjectIDFromHex(promoCodeID) + if err != nil { + s.logger.Error("Failed conversion promoCodeID to ObjectID", zap.Error(err)) + return "", err + } + + err = s.promoCodeRepo.AddFastLink(ctx, promoID, xid) + if err != nil { + s.logger.Error("Failed to add fastlink for promocode by promocode id", zap.Error(err)) + return "", err + } + + return xid, nil +} diff --git a/internal/utils/genID/gen_id.go b/internal/utils/genID/gen_id.go new file mode 100644 index 0000000..2a6610f --- /dev/null +++ b/internal/utils/genID/gen_id.go @@ -0,0 +1,8 @@ +package genID + +import "github.com/rs/xid" + +func GenerateXID() string { + id := xid.New() + return id.String() +}