add stats route

This commit is contained in:
Pavel 2024-01-27 15:51:32 +03:00
parent bfc332fd77
commit c282b8151b
8 changed files with 154 additions and 3 deletions

@ -312,7 +312,25 @@ paths:
properties:
error:
type: string
/promocode/stats:
get:
summary: Получить статистику промокода
responses:
'200':
description: Статистика промокода успешно получена
content:
application/json:
schema:
$ref: '#/components/schemas/PromoCodeStats'
'500':
description: Внутренняя ошибка сервера
content:
application/json:
schema:
type: object
properties:
error:
type: string
components:
schemas:
@ -506,3 +524,16 @@ components:
greetings:
type: string
description: Поле из активированного промокода
PromoCodeStats:
type: object
properties:
id:
type: string
description: ID промокода
usageCount:
type: object
description: Количество использований промокода для каждого пользователя
usageHistory:
type: object
description: История использования промокода для каждого пользователя

@ -67,6 +67,7 @@ func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger) error {
encrypt := initialize.Encrypt(cfg)
promoCodeRepo := repository.NewPromoCodeRepository(mdb.Collection("promoCodes"))
statsRepo := repository.NewStatsRepository(repository.Deps{Rdb: nil, Mdb: mdb.Collection("promoStats")})
codewordRepo := repository.NewCodewordRepository(repository.Deps{Rdb: rdb, Mdb: mdb.Collection("codeword")})
userRepo := repository.NewUserRepository(repository.Deps{Rdb: nil, Mdb: mdb.Collection("users")})
@ -84,6 +85,7 @@ func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger) error {
promoService := services.NewPromoCodeService(services.PromoDeps{
Logger: logger,
PromoCodeRepo: promoCodeRepo,
StatsRepo: statsRepo,
Kafka: brokers.TariffProducer,
DiscountClient: discountRpcClient,
})

@ -165,3 +165,12 @@ func (p *PromoCodeController) CreateFastLink(c *fiber.Ctx) error {
return c.Status(fiber.StatusCreated).JSON(fiber.Map{"fastlink": fastLink})
}
func (p *PromoCodeController) GetAllStats(c *fiber.Ctx) error {
promoStats, err := p.promoCodeService.GetAllStats(c.Context())
if err != nil {
p.logger.Error("Failed getting promo stats", zap.Error(err))
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
}
return c.Status(fiber.StatusOK).JSON(promoStats)
}

@ -67,3 +67,9 @@ type ActivateReq struct {
type ActivateResp struct {
Greetings string `json:"greetings"` // поле из активированного промокода
}
type PromoCodeStats struct {
ID string `bson:"_id,omitempty" json:"id,omitempty"`
UsageCount map[string]int `bson:"usageCount" json:"usageCount"`
UsageHistory map[string][]time.Time `bson:"usageHistory" json:"usageHistory"`
}

@ -0,0 +1,59 @@
package repository
import (
"codeword/internal/models"
"context"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"time"
)
type StatsRepository struct {
mdb *mongo.Collection
}
func NewStatsRepository(deps Deps) *StatsRepository {
return &StatsRepository{mdb: deps.Mdb}
}
func (r *StatsRepository) UpdateStatistics(ctx context.Context, key, userID string) error {
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}
_, err := r.mdb.UpdateOne(ctx, filter, update, opts)
return err
}
func (r *StatsRepository) GetAllStatistics(ctx context.Context) ([]models.PromoCodeStats, error) {
filter := bson.M{}
opts := options.Find()
cursor, err := r.mdb.Find(ctx, filter, opts)
if err != nil {
return nil, err
}
defer cursor.Close(ctx)
var promoCodeStatsList []models.PromoCodeStats
for cursor.Next(ctx) {
var promoCodeStats models.PromoCodeStats
if err := cursor.Decode(&promoCodeStats); err != nil {
return nil, err
}
promoCodeStatsList = append(promoCodeStatsList, promoCodeStats)
}
if err := cursor.Err(); err != nil {
return nil, err
}
return promoCodeStatsList, nil
}

@ -61,6 +61,7 @@ func (s *Server) registerRoutes() {
s.app.Post("/promocode/activate", s.PromoCodeController.Activate)
s.app.Delete("/promocode/:promocodeID", s.PromoCodeController.Delete)
s.app.Post("/promocode/fastlink", s.PromoCodeController.CreateFastLink)
s.app.Get("/promocode/stats", s.PromoCodeController.GetAllStats)
//... other
}

@ -21,9 +21,15 @@ type PromoCodeRepository interface {
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
}
@ -31,6 +37,7 @@ type PromoDeps struct {
type PromoCodeService struct {
logger *zap.Logger
promoCodeRepo PromoCodeRepository
statsRepo PromoStatsRepository
kafka *tariff.Producer
discountClient discount.DiscountServiceClient
}
@ -39,6 +46,7 @@ func NewPromoCodeService(deps PromoDeps) *PromoCodeService {
return &PromoCodeService{
logger: deps.Logger,
promoCodeRepo: deps.PromoCodeRepo,
statsRepo: deps.StatsRepo,
kafka: deps.Kafka,
discountClient: deps.DiscountClient,
}
@ -76,7 +84,7 @@ func (s *PromoCodeService) GetPromoCodesList(ctx context.Context, req *models.Ge
// todo одумать еще реализацию этого дела, надо уточнить как разделяется ответственность в бонусе между привилегией и скидкой
// разделяется ли она или они всегда вместе, если разделяются то что-то из этого может быть пустым либо все заполеннное,
//соответсвенно надо сделать соответствующие проверки до записи в кафку и до отправки в дискаунт сервис
// соответсвенно надо сделать соответствующие проверки до записи в кафку и до отправки в дискаунт сервис
func (s *PromoCodeService) ActivatePromo(ctx context.Context, req *models.ActivateReq) (string, error) {
promoCode, err := s.promoCodeRepo.ActivatePromo(ctx, req)
@ -85,6 +93,12 @@ func (s *PromoCodeService) ActivatePromo(ctx context.Context, req *models.Activa
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 != "" {
@ -159,3 +173,12 @@ func (s *PromoCodeService) CreateFastLink(ctx context.Context, promoCodeID strin
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
}

@ -533,6 +533,26 @@ func TestActivatePromoCode(t *testing.T) {
})
}
// GetPromoStats
func TestGetPromoStats(t *testing.T) {
client := fiber.AcquireClient()
t.Run("GetAllStats", func(t *testing.T) {
req := client.Get(BaseUrl + "/promocode/stats")
statusCode, resBody, errs := req.Bytes()
if len(errs) != 0 {
assert.NoError(t, errs[0])
}
assert.Equal(t, fiber.StatusOK, statusCode)
var response []models.PromoCodeStats
err := json.Unmarshal(resBody, &response)
assert.NoError(t, err)
fmt.Println(response)
})
}
// DeletePromoCode
func TestDeletePromoCode(t *testing.T) {
client := fiber.AcquireClient()