update stats
This commit is contained in:
parent
3ba931a093
commit
7d555618fb
@ -224,12 +224,12 @@ paths:
|
|||||||
description: Внутренняя ошибка сервера
|
description: Внутренняя ошибка сервера
|
||||||
|
|
||||||
/promocode/stats:
|
/promocode/stats:
|
||||||
get:
|
post:
|
||||||
operationId: GetStats
|
operationId: GetStats
|
||||||
summary: Получить статистику промокода
|
summary: Получить статистику промокода
|
||||||
tags:
|
tags:
|
||||||
- stats
|
- stats
|
||||||
description: Идентификатор промокода
|
description: Идентификатор промокода и временной интерфал от до unix
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
required: true
|
||||||
content:
|
content:
|
||||||
@ -266,37 +266,34 @@ components:
|
|||||||
PromoCodeStatsReq:
|
PromoCodeStatsReq:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
promoCodeID:
|
id:
|
||||||
type: string
|
type: string
|
||||||
|
from:
|
||||||
|
type: integer
|
||||||
|
to:
|
||||||
|
type: integer
|
||||||
required:
|
required:
|
||||||
- promoCodeID
|
- id
|
||||||
PromoCodeStatsResp:
|
PromoCodeStatsResp:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
id:
|
id:
|
||||||
type: string
|
type: string
|
||||||
description: Идентификатор промокода
|
description: id промокода
|
||||||
usageCount:
|
usageCount:
|
||||||
type: integer
|
type: integer
|
||||||
description: Количество использований промокода
|
description: общее количество использований промокода
|
||||||
|
example: 18
|
||||||
usageMap:
|
usageMap:
|
||||||
type: object
|
type: object
|
||||||
description: Карта использования промокода
|
description: мапа использования промокода ранжированая по способу
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
type: array
|
type: integer
|
||||||
items:
|
example:
|
||||||
$ref: '#/components/schemas/Usage'
|
"-": 10
|
||||||
|
"fastlinkID1": 5
|
||||||
|
"fastlinkID2": 3
|
||||||
|
|
||||||
Usage:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
key:
|
|
||||||
type: string
|
|
||||||
description: fastlink или codeword в зависимости от того что применялось
|
|
||||||
time:
|
|
||||||
type: string
|
|
||||||
format: date-time
|
|
||||||
description: Время использования промокода
|
|
||||||
|
|
||||||
CreateFastLinkReq:
|
CreateFastLinkReq:
|
||||||
type: object
|
type: object
|
||||||
|
@ -184,9 +184,7 @@ func (p *PromoCodeController) CreateFastLink(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *PromoCodeController) GetStats(c *fiber.Ctx) error {
|
func (p *PromoCodeController) GetStats(c *fiber.Ctx) error {
|
||||||
var req struct {
|
var req models.PromoStatReq
|
||||||
PromoCodeID string `json:"id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.BodyParser(&req); err != nil {
|
if err := c.BodyParser(&req); err != nil {
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||||
@ -196,7 +194,7 @@ func (p *PromoCodeController) GetStats(c *fiber.Ctx) error {
|
|||||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "PromoCode ID is required"})
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "PromoCode ID is required"})
|
||||||
}
|
}
|
||||||
|
|
||||||
promoStats, err := p.promoCodeService.GetStats(c.Context(), req.PromoCodeID)
|
promoStats, err := p.promoCodeService.GetStats(c.Context(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.logger.Error("Failed getting promo stats", zap.Error(err))
|
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.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||||
|
@ -9,7 +9,7 @@ func (p *PromoCodeController) Register(router fiber.Router) {
|
|||||||
router.Post("/activate", p.Activate)
|
router.Post("/activate", p.Activate)
|
||||||
router.Delete("/:promocodeID", p.Delete)
|
router.Delete("/:promocodeID", p.Delete)
|
||||||
router.Post("/fastlink", p.CreateFastLink)
|
router.Post("/fastlink", p.CreateFastLink)
|
||||||
router.Get("/stats", p.GetStats)
|
router.Post("/stats", p.GetStats)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,13 +81,24 @@ type ActivateResp struct {
|
|||||||
Greetings string `json:"greetings"` // поле из активированного промокода
|
Greetings string `json:"greetings"` // поле из активированного промокода
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PromoStatReq struct {
|
||||||
|
PromoCodeID string `json:"id,omitempty"`
|
||||||
|
From uint64 `json:"from"`
|
||||||
|
To uint64 `json:"to"`
|
||||||
|
}
|
||||||
|
|
||||||
type PromoCodeStats struct {
|
type PromoCodeStats struct {
|
||||||
ID string `bson:"_id,omitempty" json:"id,omitempty"`
|
ID string `json:"id,omitempty"`
|
||||||
UsageCount int `bson:"usageCount" json:"usageCount"`
|
UsageMap map[string][]Usage `json:"usageMap"`
|
||||||
UsageMap map[string][]Usage `bson:"usageMap" json:"usageMap"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Usage struct {
|
type Usage struct {
|
||||||
Key string `bson:"key" json:"key"`
|
UserID string `bson:"userID" json:"userID"`
|
||||||
Time time.Time `bson:"time" json:"time"`
|
Time uint64 `bson:"time" json:"time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PromoCodeStatsResp struct {
|
||||||
|
ID string `json:"id,omitempty"`
|
||||||
|
UsageCount int `json:"usageCount"`
|
||||||
|
UsageMap map[string]int `json:"usageMap"`
|
||||||
}
|
}
|
||||||
|
@ -20,32 +20,36 @@ func NewStatsRepository(deps Deps) *StatsRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *StatsRepository) UpdateStatistics(ctx context.Context, req *models.ActivateReq, promoCode *models.PromoCode, userID string) error {
|
func (r *StatsRepository) UpdateStatistics(ctx context.Context, req *models.ActivateReq, promoCode *models.PromoCode, userID string) error {
|
||||||
filter := bson.M{"_id": promoCode.ID, "usageMap." + userID: bson.M{"$exists": true}}
|
|
||||||
count, err := r.mdb.CountDocuments(ctx, filter)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if count >= 1 {
|
|
||||||
return ErrPromoCodeAlreadyActivated
|
|
||||||
}
|
|
||||||
|
|
||||||
var key string
|
var key string
|
||||||
if req.FastLink != "" {
|
if req.FastLink != "" {
|
||||||
key = req.FastLink
|
key = req.FastLink
|
||||||
} else {
|
} else {
|
||||||
key = req.Codeword
|
key = "-"
|
||||||
|
}
|
||||||
|
|
||||||
|
var promoCodeStats models.PromoCodeStats
|
||||||
|
err := r.mdb.FindOne(ctx, bson.M{"_id": promoCode.ID}).Decode(&promoCodeStats)
|
||||||
|
if err != nil && mongo.ErrNoDocuments == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if promoCodeStats.UsageMap != nil {
|
||||||
|
usageList := promoCodeStats.UsageMap[key]
|
||||||
|
for _, usage := range usageList {
|
||||||
|
if usage.UserID == userID {
|
||||||
|
return ErrPromoCodeAlreadyActivated
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
usage := models.Usage{
|
usage := models.Usage{
|
||||||
Key: key,
|
UserID: userID,
|
||||||
Time: time.Now(),
|
Time: uint64(time.Now().Unix()),
|
||||||
}
|
}
|
||||||
|
|
||||||
update := bson.M{
|
update := bson.M{
|
||||||
"$inc": bson.M{"usageCount": 1},
|
|
||||||
"$push": bson.M{
|
"$push": bson.M{
|
||||||
"usageMap." + userID: usage,
|
"usageMap." + key: usage,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,10 +58,10 @@ func (r *StatsRepository) UpdateStatistics(ctx context.Context, req *models.Acti
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *StatsRepository) GetStatistics(ctx context.Context, promoCodeID string) (*models.PromoCodeStats, error) {
|
func (r *StatsRepository) GetStatistics(ctx context.Context, promoCodeID string) (models.PromoCodeStats, error) {
|
||||||
objID, err := primitive.ObjectIDFromHex(promoCodeID)
|
objID, err := primitive.ObjectIDFromHex(promoCodeID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return models.PromoCodeStats{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
filter := bson.M{"_id": objID}
|
filter := bson.M{"_id": objID}
|
||||||
@ -65,8 +69,8 @@ func (r *StatsRepository) GetStatistics(ctx context.Context, promoCodeID string)
|
|||||||
var promoCodeStats models.PromoCodeStats
|
var promoCodeStats models.PromoCodeStats
|
||||||
err = r.mdb.FindOne(ctx, filter).Decode(&promoCodeStats)
|
err = r.mdb.FindOne(ctx, filter).Decode(&promoCodeStats)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return models.PromoCodeStats{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &promoCodeStats, nil
|
return promoCodeStats, nil
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ type PromoCodeRepository interface {
|
|||||||
|
|
||||||
type PromoStatsRepository interface {
|
type PromoStatsRepository interface {
|
||||||
UpdateStatistics(ctx context.Context, req *models.ActivateReq, promoCode *models.PromoCode, userID string) error
|
UpdateStatistics(ctx context.Context, req *models.ActivateReq, promoCode *models.PromoCode, userID string) error
|
||||||
GetStatistics(ctx context.Context, promoCodeID string) (*models.PromoCodeStats, error)
|
GetStatistics(ctx context.Context, promoCodeID string) (models.PromoCodeStats, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type PromoDeps struct {
|
type PromoDeps struct {
|
||||||
@ -201,11 +201,35 @@ func (s *PromoCodeService) CreateFastLink(ctx context.Context, promoCodeID strin
|
|||||||
return xid, nil
|
return xid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PromoCodeService) GetStats(ctx context.Context, promoCodeID string) (*models.PromoCodeStats, error) {
|
func (s *PromoCodeService) GetStats(ctx context.Context, req models.PromoStatReq) (models.PromoCodeStatsResp, error) {
|
||||||
promoStats, err := s.statsRepo.GetStatistics(ctx, promoCodeID)
|
promoStats, err := s.statsRepo.GetStatistics(ctx, req.PromoCodeID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed getting promo stats", zap.Error(err))
|
s.logger.Error("Failed getting promo stats", zap.Error(err))
|
||||||
return nil, err
|
return models.PromoCodeStatsResp{}, err
|
||||||
}
|
}
|
||||||
return promoStats, nil
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user