добавлены разграничения в ошибках, убрана транзакция
This commit is contained in:
parent
cbd82e8779
commit
ca0e4f7708
@ -103,7 +103,6 @@ func (p *PromoCodeController) Activate(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
userID := c.Locals(models.AuthJWTDecodedUserIDKey).(string)
|
||||
|
||||
if userID == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "failed to get jwt payload"})
|
||||
}
|
||||
@ -121,12 +120,19 @@ func (p *PromoCodeController) Activate(c *fiber.Ctx) error {
|
||||
if err != nil {
|
||||
p.logger.Error("Failed to activate promocode", zap.Error(err))
|
||||
|
||||
if errors.Is(err, repository.ErrPromoCodeNotFound) {
|
||||
switch {
|
||||
case errors.Is(err, repository.ErrPromoCodeNotFound):
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "PromoCode not found"})
|
||||
}
|
||||
|
||||
case errors.Is(err, repository.ErrPromoCodeAlreadyActivated):
|
||||
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "PromoCode already activated"})
|
||||
case errors.Is(err, repository.ErrPromoCodeExpired):
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
|
||||
case errors.Is(err, repository.ErrPromoCodeExhausted):
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "PromoCode exhausted"})
|
||||
default:
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||
}
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(models.ActivateResp{Greetings: greetings})
|
||||
}
|
||||
|
||||
@ -7,4 +7,7 @@ var (
|
||||
ErrAlreadyReported = errors.New("already reported")
|
||||
ErrDuplicateCodeword = errors.New("duplicate codeword")
|
||||
ErrPromoCodeNotFound = errors.New("promo code not found")
|
||||
ErrPromoCodeExpired = errors.New("promo code is expired")
|
||||
ErrPromoCodeExhausted = errors.New("promo code is exhausted")
|
||||
ErrPromoCodeAlreadyActivated = errors.New("promo code is already activated")
|
||||
)
|
||||
|
||||
@ -187,61 +187,50 @@ func (r *PromoCodeRepository) GetPromoCodesList(ctx context.Context, req *models
|
||||
}
|
||||
|
||||
func (r *PromoCodeRepository) ActivatePromo(ctx context.Context, req *models.ActivateReq) (*models.PromoCode, error) {
|
||||
session, err := r.mdb.Database().Client().StartSession()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer session.EndSession(ctx)
|
||||
|
||||
var promoCode models.PromoCode
|
||||
|
||||
transactionErr := mongo.WithSession(ctx, session, func(sc mongo.SessionContext) error {
|
||||
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()},
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
err := r.mdb.FindOneAndUpdate(ctx, filter, bson.M{"$inc": bson.M{"activationCount": -1}}, opts).Decode(&promoCode)
|
||||
if err != nil {
|
||||
if err == mongo.ErrNoDocuments {
|
||||
return ErrPromoCodeNotFound
|
||||
return nil, ErrPromoCodeNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if promoCode.ActivationCount <= 0 && promoCode.DueTo > time.Now().Unix() {
|
||||
if !promoCode.OffLimit {
|
||||
update := bson.M{"$set": bson.M{"offLimit": true}}
|
||||
_, err := r.mdb.UpdateOne(ctx, filter, update)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return &promoCode, nil
|
||||
}
|
||||
|
||||
func (r *PromoCodeRepository) IncreaseActivationCount(ctx context.Context, promoCodeID primitive.ObjectID) error {
|
||||
filter := bson.M{"_id": promoCodeID}
|
||||
update := bson.M{"$inc": bson.M{"activationCount": 1}}
|
||||
_, err := r.mdb.UpdateOne(ctx, filter, update)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
promoCode = updatedPromoCode
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if transactionErr != nil {
|
||||
return nil, transactionErr
|
||||
}
|
||||
|
||||
return &promoCode, nil
|
||||
}
|
||||
|
||||
func (r *PromoCodeRepository) DeletePromoCode(ctx context.Context, promoCodeID string) error {
|
||||
|
||||
@ -19,15 +19,26 @@ func NewStatsRepository(deps Deps) *StatsRepository {
|
||||
}
|
||||
|
||||
func (r *StatsRepository) UpdateStatistics(ctx context.Context, key, userID string) error {
|
||||
filter := bson.M{"_id": key, "usageCount." + userID: bson.M{"$exists": true}}
|
||||
|
||||
count, err := r.mdb.CountDocuments(ctx, filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
return ErrPromoCodeAlreadyActivated
|
||||
}
|
||||
|
||||
update := bson.M{
|
||||
"$inc": bson.M{"usageCount." + userID: 1},
|
||||
"$push": bson.M{"usageHistory." + userID: time.Now()},
|
||||
}
|
||||
|
||||
opts := options.Update().SetUpsert(true)
|
||||
filter := bson.M{"_id": key}
|
||||
filter = bson.M{"_id": key}
|
||||
|
||||
_, err := r.mdb.UpdateOne(ctx, filter, update, opts)
|
||||
_, err = r.mdb.UpdateOne(ctx, filter, update, opts)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@ -4,11 +4,14 @@ import (
|
||||
"codeword/internal/kafka/tariff"
|
||||
"codeword/internal/models"
|
||||
"codeword/internal/proto/discount"
|
||||
"codeword/internal/repository"
|
||||
"codeword/internal/utils/genID"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.uber.org/zap"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PromoCodeRepository interface {
|
||||
@ -19,6 +22,7 @@ type PromoCodeRepository interface {
|
||||
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
|
||||
IncreaseActivationCount(ctx context.Context, promoCodeID primitive.ObjectID) error
|
||||
}
|
||||
|
||||
type PromoStatsRepository interface {
|
||||
@ -92,9 +96,32 @@ func (s *PromoCodeService) ActivatePromo(ctx context.Context, req *models.Activa
|
||||
s.logger.Error("Failed to activate promocode", zap.Error(err))
|
||||
return "", err
|
||||
}
|
||||
//todo такая реализация проверок кажется довольно массивной, думаю как то это стоит сделать параллельно обхаживая все условия
|
||||
if promoCode.DueTo < time.Now().Unix() && promoCode.OffLimit {
|
||||
err := s.promoCodeRepo.IncreaseActivationCount(ctx, promoCode.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "", fmt.Errorf("%w: expired on %s", repository.ErrPromoCodeExpired, time.Unix(promoCode.DueTo, 0).Format(time.RFC3339))
|
||||
}
|
||||
|
||||
if promoCode.DueTo == 0 && promoCode.ActivationCount < 0 {
|
||||
err := s.promoCodeRepo.IncreaseActivationCount(ctx, promoCode.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "", repository.ErrPromoCodeExhausted
|
||||
}
|
||||
|
||||
err = s.statsRepo.UpdateStatistics(ctx, promoCode.ID.Hex(), userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, repository.ErrPromoCodeAlreadyActivated) {
|
||||
err := s.promoCodeRepo.IncreaseActivationCount(ctx, promoCode.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "", repository.ErrPromoCodeAlreadyActivated
|
||||
}
|
||||
s.logger.Error("Failed add in stats", zap.Error(err))
|
||||
return "", err
|
||||
}
|
||||
|
||||
@ -466,7 +466,6 @@ func TestActivatePromoCode(t *testing.T) {
|
||||
}
|
||||
|
||||
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||
|
||||
var response models.ActivateResp
|
||||
err := json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user