271 lines
8.9 KiB
Go
271 lines
8.9 KiB
Go
package services
|
|
|
|
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 {
|
|
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
|
|
IncreaseActivationCount(ctx context.Context, promoCodeID primitive.ObjectID) error
|
|
}
|
|
|
|
type PromoStatsRepository interface {
|
|
UpdateStatistics(ctx context.Context, req *models.ActivateReq, promoCode *models.PromoCode, userID string) error
|
|
GetStatistics(ctx context.Context, promoCodeID string) (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, userID string) (string, error) {
|
|
promoCode, err := s.promoCodeRepo.ActivatePromo(ctx, req)
|
|
fmt.Println("SKER20", err, promoCode)
|
|
if err != nil {
|
|
s.logger.Error("Failed to activate promocode", zap.Error(err))
|
|
return "", err
|
|
}
|
|
//todo такая реализация проверок кажется довольно массивной, думаю как то это стоит сделать параллельно обхаживая все условия
|
|
if promoCode.DueTo < time.Now().Unix() && promoCode.DueTo > 0 && promoCode.ActivationLimit != 0 {
|
|
err := s.promoCodeRepo.IncreaseActivationCount(ctx, promoCode.ID)
|
|
fmt.Println("SKER21", err)
|
|
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 && promoCode.ActivationLimit != 0 {
|
|
err := s.promoCodeRepo.IncreaseActivationCount(ctx, promoCode.ID)
|
|
fmt.Println("SKER22", err)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return "", repository.ErrPromoCodeExhausted
|
|
}
|
|
|
|
err = s.statsRepo.UpdateStatistics(ctx, req, promoCode, userID)
|
|
fmt.Println("SKER23", err)
|
|
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
|
|
}
|
|
|
|
var postfix string
|
|
|
|
if req.FastLink != "" {
|
|
postfix = fmt.Sprintf(":(%s)", req.FastLink)
|
|
}
|
|
|
|
if promoCode.Bonus.Privilege.PrivilegeID != "" {
|
|
|
|
var privileges []models.Privilege
|
|
privilege := models.Privilege{
|
|
PrivilegeID: promoCode.Bonus.Privilege.PrivilegeID,
|
|
Amount: promoCode.Bonus.Privilege.Amount,
|
|
ServiceKey: promoCode.Bonus.Privilege.ServiceKey,
|
|
}
|
|
privileges = append(privileges, privilege)
|
|
|
|
fakeTariff := &models.Tariff{
|
|
Name: promoCode.Codeword + postfix,
|
|
Privileges: privileges,
|
|
Deleted: promoCode.Delete,
|
|
CreatedAt: promoCode.CreatedAt,
|
|
}
|
|
fmt.Println("SKER24", err)
|
|
if err := s.kafka.Send(ctx, userID, fakeTariff); err != nil {
|
|
s.logger.Error("Failed to send fake tariff to Kafka", zap.Error(err))
|
|
return "", err
|
|
}
|
|
}
|
|
|
|
if promoCode.Bonus.Discount.Factor != 0 {
|
|
disOverHelm := true
|
|
emptyString := ""
|
|
zero := uint64(0)
|
|
discountRequest := &discount.CreateDiscountRequest{
|
|
Name: promoCode.Codeword + postfix,
|
|
Layer: promoCode.Bonus.Discount.Layer,
|
|
Description: "",
|
|
Condition: &discount.DiscountCondition{
|
|
Coupon: &promoCode.Codeword,
|
|
User: &userID,
|
|
Group: &promoCode.Bonus.Discount.Target,
|
|
Product: &promoCode.Bonus.Discount.Target,
|
|
UserType: &emptyString,
|
|
PurchasesAmount: &zero,
|
|
CartPurchasesAmount: &zero,
|
|
Term: &zero,
|
|
Usage: &zero,
|
|
PriceFrom: &zero,
|
|
},
|
|
}
|
|
|
|
if promoCode.Bonus.Discount.Layer == 1 {
|
|
discountRequest.Target = &discount.DiscountCalculationTarget{
|
|
Products: []*discount.ProductTarget{{
|
|
ID: promoCode.Bonus.Discount.Target,
|
|
Factor: promoCode.Bonus.Discount.Factor,
|
|
}},
|
|
Overhelm: &disOverHelm,
|
|
}
|
|
}
|
|
|
|
if promoCode.Bonus.Discount.Layer == 2 {
|
|
discountRequest.Target = &discount.DiscountCalculationTarget{
|
|
TargetGroup: &promoCode.Bonus.Discount.Target,
|
|
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) GetStats(ctx context.Context, req models.PromoStatReq) (models.PromoCodeStatsResp, error) {
|
|
promoStats, err := s.statsRepo.GetStatistics(ctx, req.PromoCodeID)
|
|
if err != nil {
|
|
s.logger.Error("Failed getting promo stats", zap.Error(err))
|
|
return models.PromoCodeStatsResp{}, err
|
|
}
|
|
|
|
var resp models.PromoCodeStatsResp
|
|
|
|
stats := make(map[string]int)
|
|
|
|
for key, usageCount := range promoStats.UsageMap {
|
|
count := 0
|
|
for _, usage := range usageCount {
|
|
if (req.From == 0 || usage.Time >= req.From) && (req.To == 0 || usage.Time <= req.To) {
|
|
count++
|
|
}
|
|
}
|
|
stats[key] = count
|
|
}
|
|
|
|
totalUsageCount := 0
|
|
for _, count := range stats {
|
|
totalUsageCount += count
|
|
}
|
|
|
|
resp.UsageMap = stats
|
|
resp.UsageCount = totalUsageCount
|
|
resp.ID = req.PromoCodeID
|
|
|
|
return resp, nil
|
|
}
|