2024-01-27 12:51:32 +00:00
|
|
|
package repository
|
|
|
|
|
|
|
|
import (
|
|
|
|
"codeword/internal/models"
|
2024-04-25 12:09:28 +00:00
|
|
|
codeword_rpc "codeword/internal/proto/codeword"
|
2024-01-27 12:51:32 +00:00
|
|
|
"context"
|
|
|
|
"go.mongodb.org/mongo-driver/bson"
|
2024-03-03 18:24:28 +00:00
|
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
2024-01-27 12:51:32 +00:00
|
|
|
"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}
|
|
|
|
}
|
|
|
|
|
2024-03-03 18:24:28 +00:00
|
|
|
func (r *StatsRepository) UpdateStatistics(ctx context.Context, req *models.ActivateReq, promoCode *models.PromoCode, userID string) error {
|
|
|
|
var key string
|
|
|
|
if req.FastLink != "" {
|
|
|
|
key = req.FastLink
|
|
|
|
} else {
|
2024-03-27 12:29:49 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
2024-03-03 18:24:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
usage := models.Usage{
|
2024-03-27 12:29:49 +00:00
|
|
|
UserID: userID,
|
|
|
|
Time: uint64(time.Now().Unix()),
|
2024-03-03 18:24:28 +00:00
|
|
|
}
|
|
|
|
|
2024-01-27 12:51:32 +00:00
|
|
|
update := bson.M{
|
2024-03-03 18:24:28 +00:00
|
|
|
"$push": bson.M{
|
2024-03-27 12:29:49 +00:00
|
|
|
"usageMap." + key: usage,
|
2024-03-03 18:24:28 +00:00
|
|
|
},
|
2024-01-27 12:51:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
opts := options.Update().SetUpsert(true)
|
2024-03-03 18:24:28 +00:00
|
|
|
_, err = r.mdb.UpdateOne(ctx, bson.M{"_id": promoCode.ID}, update, opts)
|
2024-01-27 12:51:32 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-03-27 12:29:49 +00:00
|
|
|
func (r *StatsRepository) GetStatistics(ctx context.Context, promoCodeID string) (models.PromoCodeStats, error) {
|
2024-03-03 18:24:28 +00:00
|
|
|
objID, err := primitive.ObjectIDFromHex(promoCodeID)
|
2024-01-27 12:51:32 +00:00
|
|
|
if err != nil {
|
2024-03-27 12:29:49 +00:00
|
|
|
return models.PromoCodeStats{}, err
|
2024-01-27 12:51:32 +00:00
|
|
|
}
|
|
|
|
|
2024-03-03 18:24:28 +00:00
|
|
|
filter := bson.M{"_id": objID}
|
2024-01-27 12:51:32 +00:00
|
|
|
|
2024-03-03 18:24:28 +00:00
|
|
|
var promoCodeStats models.PromoCodeStats
|
|
|
|
err = r.mdb.FindOne(ctx, filter).Decode(&promoCodeStats)
|
|
|
|
if err != nil {
|
2024-04-25 10:57:26 +00:00
|
|
|
if err == mongo.ErrNoDocuments {
|
|
|
|
return models.PromoCodeStats{}, nil
|
|
|
|
}
|
2024-03-27 12:29:49 +00:00
|
|
|
return models.PromoCodeStats{}, err
|
2024-01-27 12:51:32 +00:00
|
|
|
}
|
|
|
|
|
2024-03-27 12:29:49 +00:00
|
|
|
return promoCodeStats, nil
|
2024-01-27 12:51:32 +00:00
|
|
|
}
|
2024-04-25 10:57:26 +00:00
|
|
|
|
2024-04-26 07:39:04 +00:00
|
|
|
func (r *StatsRepository) GetAllPromoActivations(ctx context.Context, req *codeword_rpc.Time) (*codeword_rpc.PromoActivationResp, error) {
|
|
|
|
var pipeline []bson.M
|
|
|
|
pipeline = append(pipeline, []bson.M{
|
2024-04-25 10:57:26 +00:00
|
|
|
{
|
|
|
|
"$project": bson.M{
|
|
|
|
"_id": 1,
|
|
|
|
"usageArray": bson.M{"$objectToArray": "$usageMap"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"$unwind": "$usageArray",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"$unwind": "$usageArray.v",
|
|
|
|
},
|
2024-04-26 07:39:04 +00:00
|
|
|
}...)
|
|
|
|
|
|
|
|
if req.To != 0 && req.From != 0 {
|
|
|
|
pipeline = append(pipeline, bson.M{
|
|
|
|
"$match": bson.M{
|
|
|
|
"usageArray.v.time": bson.M{
|
|
|
|
"$gte": req.From,
|
|
|
|
"$lte": req.To,
|
|
|
|
},
|
2024-04-25 10:57:26 +00:00
|
|
|
},
|
2024-04-26 07:39:04 +00:00
|
|
|
})
|
2024-04-25 10:57:26 +00:00
|
|
|
}
|
|
|
|
|
2024-04-26 07:39:04 +00:00
|
|
|
pipeline = append(pipeline, bson.M{
|
|
|
|
"$group": bson.M{
|
|
|
|
"_id": "$_id",
|
|
|
|
"users": bson.M{"$push": bson.M{
|
|
|
|
"UserID": "$usageArray.v.userID",
|
|
|
|
"Time": "$usageArray.v.time",
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
2024-04-25 10:57:26 +00:00
|
|
|
cursor, err := r.mdb.Aggregate(ctx, pipeline)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2024-04-25 12:09:28 +00:00
|
|
|
result := make(map[string]*codeword_rpc.PromoActivationResp_Activations)
|
2024-04-25 10:57:26 +00:00
|
|
|
for cursor.Next(ctx) {
|
|
|
|
var data struct {
|
|
|
|
ID string `bson:"_id"`
|
|
|
|
Users []struct {
|
|
|
|
UserID string `bson:"UserID"`
|
2024-04-25 17:44:32 +00:00
|
|
|
Time int64
|
2024-04-25 10:57:26 +00:00
|
|
|
} `bson:"users"`
|
|
|
|
}
|
|
|
|
err := cursor.Decode(&data)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-04-25 12:09:28 +00:00
|
|
|
|
|
|
|
if _, ok := result[data.ID]; !ok {
|
|
|
|
result[data.ID] = &codeword_rpc.PromoActivationResp_Activations{}
|
|
|
|
}
|
|
|
|
|
2024-04-25 10:57:26 +00:00
|
|
|
for _, user := range data.Users {
|
2024-04-25 17:55:07 +00:00
|
|
|
result[data.ID].Values = append(result[data.ID].Values, &codeword_rpc.PromoActivationResp_UserTime{
|
|
|
|
UserID: user.UserID,
|
|
|
|
Time: user.Time,
|
|
|
|
})
|
2024-04-25 10:57:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-25 12:09:28 +00:00
|
|
|
return &codeword_rpc.PromoActivationResp{
|
|
|
|
Response: result,
|
|
|
|
}, nil
|
2024-04-25 10:57:26 +00:00
|
|
|
}
|