change promo history
This commit is contained in:
parent
ca0e4f7708
commit
0b17ab396f
@ -313,8 +313,18 @@ paths:
|
|||||||
error:
|
error:
|
||||||
type: string
|
type: string
|
||||||
/promocode/stats:
|
/promocode/stats:
|
||||||
get:
|
post:
|
||||||
summary: Получить статистику промокода
|
summary: Получить статистику промокода
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
promoCodeID:
|
||||||
|
type: string
|
||||||
|
description: Идентификатор промокода
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Статистика промокода успешно получена
|
description: Статистика промокода успешно получена
|
||||||
@ -322,6 +332,15 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/PromoCodeStats'
|
$ref: '#/components/schemas/PromoCodeStats'
|
||||||
|
'400':
|
||||||
|
description: Неверный запрос
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
'500':
|
'500':
|
||||||
description: Внутренняя ошибка сервера
|
description: Внутренняя ошибка сервера
|
||||||
content:
|
content:
|
||||||
|
@ -184,8 +184,16 @@ func (p *PromoCodeController) CreateFastLink(c *fiber.Ctx) error {
|
|||||||
return c.Status(fiber.StatusCreated).JSON(fiber.Map{"fastlink": fastLink})
|
return c.Status(fiber.StatusCreated).JSON(fiber.Map{"fastlink": fastLink})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PromoCodeController) GetAllStats(c *fiber.Ctx) error {
|
func (p *PromoCodeController) GetStats(c *fiber.Ctx) error {
|
||||||
promoStats, err := p.promoCodeService.GetAllStats(c.Context())
|
var req struct {
|
||||||
|
PromoCodeID string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.BodyParser(&req); err != nil {
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||||
|
}
|
||||||
|
|
||||||
|
promoStats, err := p.promoCodeService.GetStats(c.Context(), req.PromoCodeID)
|
||||||
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.GetAllStats)
|
router.Get("/stats", p.GetStats)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +68,12 @@ type ActivateResp struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PromoCodeStats struct {
|
type PromoCodeStats struct {
|
||||||
ID string `bson:"_id,omitempty" json:"id,omitempty"`
|
ID string `bson:"_id,omitempty" json:"id,omitempty"`
|
||||||
UsageCount map[string]int `bson:"usageCount" json:"usageCount"`
|
UsageCount int `bson:"usageCount" json:"usageCount"`
|
||||||
UsageHistory map[string][]time.Time `bson:"usageHistory" json:"usageHistory"`
|
UsageMap map[string][]Usage `bson:"usageMap" json:"usageMap"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Usage struct {
|
||||||
|
Key string `bson:"key" json:"key"`
|
||||||
|
Time time.Time `bson:"time" json:"time"`
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"codeword/internal/models"
|
"codeword/internal/models"
|
||||||
"context"
|
"context"
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
"go.mongodb.org/mongo-driver/mongo/options"
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
"time"
|
"time"
|
||||||
@ -18,53 +19,54 @@ func NewStatsRepository(deps Deps) *StatsRepository {
|
|||||||
return &StatsRepository{mdb: deps.Mdb}
|
return &StatsRepository{mdb: deps.Mdb}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *StatsRepository) UpdateStatistics(ctx context.Context, key, userID string) error {
|
func (r *StatsRepository) UpdateStatistics(ctx context.Context, req *models.ActivateReq, promoCode *models.PromoCode, userID string) error {
|
||||||
filter := bson.M{"_id": key, "usageCount." + userID: bson.M{"$exists": true}}
|
filter := bson.M{"_id": promoCode.ID, "usageMap." + userID: bson.M{"$exists": true}}
|
||||||
|
|
||||||
count, err := r.mdb.CountDocuments(ctx, filter)
|
count, err := r.mdb.CountDocuments(ctx, filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if count > 0 {
|
if count == 1 {
|
||||||
return ErrPromoCodeAlreadyActivated
|
return ErrPromoCodeAlreadyActivated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var key string
|
||||||
|
if req.FastLink != "" {
|
||||||
|
key = req.FastLink
|
||||||
|
} else {
|
||||||
|
key = req.Codeword
|
||||||
|
}
|
||||||
|
|
||||||
|
usage := models.Usage{
|
||||||
|
Key: key,
|
||||||
|
Time: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
update := bson.M{
|
update := bson.M{
|
||||||
"$inc": bson.M{"usageCount." + userID: 1},
|
"$inc": bson.M{"usageCount": 1},
|
||||||
"$push": bson.M{"usageHistory." + userID: time.Now()},
|
"$push": bson.M{
|
||||||
|
"usageMap." + userID: usage,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := options.Update().SetUpsert(true)
|
opts := options.Update().SetUpsert(true)
|
||||||
filter = bson.M{"_id": key}
|
_, err = r.mdb.UpdateOne(ctx, bson.M{"_id": promoCode.ID}, update, opts)
|
||||||
|
|
||||||
_, err = r.mdb.UpdateOne(ctx, filter, update, opts)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *StatsRepository) GetAllStatistics(ctx context.Context) ([]models.PromoCodeStats, error) {
|
func (r *StatsRepository) GetStatistics(ctx context.Context, promoCodeID string) (*models.PromoCodeStats, error) {
|
||||||
filter := bson.M{}
|
objID, err := primitive.ObjectIDFromHex(promoCodeID)
|
||||||
|
|
||||||
opts := options.Find()
|
|
||||||
|
|
||||||
cursor, err := r.mdb.Find(ctx, filter, opts)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer cursor.Close(ctx)
|
|
||||||
|
|
||||||
var promoCodeStatsList []models.PromoCodeStats
|
filter := bson.M{"_id": objID}
|
||||||
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 {
|
var promoCodeStats models.PromoCodeStats
|
||||||
|
err = r.mdb.FindOne(ctx, filter).Decode(&promoCodeStats)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return promoCodeStatsList, nil
|
return &promoCodeStats, nil
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,8 @@ type PromoCodeRepository interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PromoStatsRepository interface {
|
type PromoStatsRepository interface {
|
||||||
UpdateStatistics(ctx context.Context, key, userID string) error
|
UpdateStatistics(ctx context.Context, req *models.ActivateReq, promoCode *models.PromoCode, userID string) error
|
||||||
GetAllStatistics(ctx context.Context) ([]models.PromoCodeStats, error)
|
GetStatistics(ctx context.Context, promoCodeID string) (*models.PromoCodeStats, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type PromoDeps struct {
|
type PromoDeps struct {
|
||||||
@ -113,7 +113,7 @@ func (s *PromoCodeService) ActivatePromo(ctx context.Context, req *models.Activa
|
|||||||
return "", repository.ErrPromoCodeExhausted
|
return "", repository.ErrPromoCodeExhausted
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.statsRepo.UpdateStatistics(ctx, promoCode.ID.Hex(), userID)
|
err = s.statsRepo.UpdateStatistics(ctx, req, promoCode, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, repository.ErrPromoCodeAlreadyActivated) {
|
if errors.Is(err, repository.ErrPromoCodeAlreadyActivated) {
|
||||||
err := s.promoCodeRepo.IncreaseActivationCount(ctx, promoCode.ID)
|
err := s.promoCodeRepo.IncreaseActivationCount(ctx, promoCode.ID)
|
||||||
@ -201,8 +201,8 @@ func (s *PromoCodeService) CreateFastLink(ctx context.Context, promoCodeID strin
|
|||||||
return xid, nil
|
return xid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PromoCodeService) GetAllStats(ctx context.Context) ([]models.PromoCodeStats, error) {
|
func (s *PromoCodeService) GetStats(ctx context.Context, promoCodeID string) (*models.PromoCodeStats, error) {
|
||||||
promoStats, err := s.statsRepo.GetAllStatistics(ctx)
|
promoStats, err := s.statsRepo.GetStatistics(ctx, 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 nil, err
|
||||||
|
@ -316,7 +316,11 @@ func TestCreateFastLink(t *testing.T) {
|
|||||||
client := fiber.AcquireClient()
|
client := fiber.AcquireClient()
|
||||||
|
|
||||||
t.Run("CreateFastLink-success", func(t *testing.T) {
|
t.Run("CreateFastLink-success", func(t *testing.T) {
|
||||||
reqBody := map[string]string{"id": promoID}
|
reqBody := struct {
|
||||||
|
PromoCodeID string `json:"id"`
|
||||||
|
}{
|
||||||
|
PromoCodeID: promoID,
|
||||||
|
}
|
||||||
|
|
||||||
reqJSON, _ := json.Marshal(reqBody)
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
|
||||||
@ -326,7 +330,7 @@ func TestCreateFastLink(t *testing.T) {
|
|||||||
if len(errs) != 0 {
|
if len(errs) != 0 {
|
||||||
assert.NoError(t, errs[0])
|
assert.NoError(t, errs[0])
|
||||||
}
|
}
|
||||||
|
fmt.Println(string(resBody))
|
||||||
assert.Equal(t, fiber.StatusCreated, statusCode)
|
assert.Equal(t, fiber.StatusCreated, statusCode)
|
||||||
|
|
||||||
var response map[string]string
|
var response map[string]string
|
||||||
@ -537,7 +541,15 @@ func TestGetPromoStats(t *testing.T) {
|
|||||||
client := fiber.AcquireClient()
|
client := fiber.AcquireClient()
|
||||||
|
|
||||||
t.Run("GetAllStats", func(t *testing.T) {
|
t.Run("GetAllStats", func(t *testing.T) {
|
||||||
req := client.Get(BaseUrl + "/promocode/stats")
|
|
||||||
|
reqBody := struct {
|
||||||
|
PromoCodeID string `json:"id"`
|
||||||
|
}{
|
||||||
|
PromoCodeID: promoID,
|
||||||
|
}
|
||||||
|
|
||||||
|
reqJSON, _ := json.Marshal(reqBody)
|
||||||
|
req := client.Get(BaseUrl+"/promocode/stats").Set("Content-Type", "application/json").Body(reqJSON)
|
||||||
|
|
||||||
statusCode, resBody, errs := req.Bytes()
|
statusCode, resBody, errs := req.Bytes()
|
||||||
if len(errs) != 0 {
|
if len(errs) != 0 {
|
||||||
|
Loading…
Reference in New Issue
Block a user