package services import ( "codeword/internal/kafka/tariff" "codeword/internal/models" "codeword/internal/proto/discount" "codeword/internal/utils/genID" "context" "fmt" "go.mongodb.org/mongo-driver/bson/primitive" "go.uber.org/zap" ) 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) (*models.PromoCode, 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 PromoStatsRepository interface { UpdateStatistics(ctx context.Context, key, userID string) error GetAllStatistics(ctx context.Context) ([]models.PromoCodeStats, error) } type PromoDeps struct { Logger *zap.Logger PromoCodeRepo PromoCodeRepository StatsRepo PromoStatsRepository Kafka *tariff.Producer DiscountClient discount.DiscountServiceClient } type PromoCodeService struct { logger *zap.Logger promoCodeRepo PromoCodeRepository statsRepo PromoStatsRepository kafka *tariff.Producer discountClient discount.DiscountServiceClient } func NewPromoCodeService(deps PromoDeps) *PromoCodeService { return &PromoCodeService{ logger: deps.Logger, promoCodeRepo: deps.PromoCodeRepo, statsRepo: deps.StatsRepo, kafka: deps.Kafka, discountClient: deps.DiscountClient, } } func (s *PromoCodeService) CreatePromoCode(ctx context.Context, req *models.PromoCode) (*models.PromoCode, error) { promoCode, err := s.promoCodeRepo.CreatePromoCode(ctx, req) if err != nil { s.logger.Error("Failed to add promocode in database", zap.Error(err)) return nil, err } return promoCode, nil } func (s *PromoCodeService) EditPromoCode(ctx context.Context, req *models.ReqEditPromoCode) (*models.PromoCode, error) { editedPromoCode, err := s.promoCodeRepo.EditPromoCode(ctx, req) if err != nil { s.logger.Error("Failed to edit promocode in database", zap.Error(err)) return nil, err } 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 } // todo одумать еще реализацию этого дела, надо уточнить как разделяется ответственность в бонусе между привилегией и скидкой // разделяется ли она или они всегда вместе, если разделяются то что-то из этого может быть пустым либо все заполеннное, // соответсвенно надо сделать соответствующие проверки до записи в кафку и до отправки в дискаунт сервис func (s *PromoCodeService) ActivatePromo(ctx context.Context, req *models.ActivateReq) (string, error) { promoCode, err := s.promoCodeRepo.ActivatePromo(ctx, req) if err != nil { s.logger.Error("Failed to activate promocode", zap.Error(err)) return "", err } err = s.statsRepo.UpdateStatistics(ctx, promoCode.ID.Hex(), req.UserID) if err != nil { s.logger.Error("Failed add in stats", zap.Error(err)) return "", err } var postfix string if req.FastLink != "" { postfix = fmt.Sprintf(":(%s)", req.FastLink) } var privileges []models.Privilege privilege := models.Privilege{ PrivilegeID: promoCode.Bonus.Privilege.PrivilegeID, Amount: promoCode.Bonus.Privilege.Amount, } privileges = append(privileges, privilege) fakeTariff := &models.Tariff{ Name: promoCode.Codeword + postfix, Privileges: privileges, Deleted: promoCode.Delete, CreatedAt: promoCode.CreatedAt, } if err := s.kafka.Send(ctx, req.UserID, fakeTariff); err != nil { s.logger.Error("Failed to send fake tariff to Kafka", zap.Error(err)) return "", err } disOverHelm := true discountRequest := &discount.CreateDiscountRequest{ Name: promoCode.Codeword + postfix, Layer: promoCode.Bonus.Discount.Layer, Description: "", Condition: &discount.DiscountCondition{ Coupon: &promoCode.Codeword, User: &req.UserID, }, Target: &discount.DiscountCalculationTarget{ Factor: promoCode.Bonus.Discount.Factor, Overhelm: &disOverHelm, }, } _, err = s.discountClient.CreateDiscount(ctx, discountRequest) if err != nil { s.logger.Error("Failed to create discount", zap.Error(err)) return "", err } return promoCode.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 } 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 } func (s *PromoCodeService) GetAllStats(ctx context.Context) ([]models.PromoCodeStats, error) { promoStats, err := s.statsRepo.GetAllStatistics(ctx) if err != nil { s.logger.Error("Failed getting promo stats", zap.Error(err)) return nil, err } return promoStats, nil }