301 lines
7.3 KiB
Go
301 lines
7.3 KiB
Go
package repository
|
|
|
|
import (
|
|
"codeword/internal/models"
|
|
"context"
|
|
"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/options"
|
|
"time"
|
|
)
|
|
|
|
// структура для горутины чтобы ошибки не пропускать
|
|
type countResult struct {
|
|
count int64
|
|
err error
|
|
}
|
|
|
|
type PromoCodeRepository struct {
|
|
mdb *mongo.Collection
|
|
}
|
|
|
|
func NewPromoCodeRepository(mdb *mongo.Collection) *PromoCodeRepository {
|
|
return &PromoCodeRepository{mdb: mdb}
|
|
}
|
|
|
|
func InitPromoCodeIndexes(ctx context.Context, mdb *mongo.Collection) error {
|
|
uniqueIndexModel := mongo.IndexModel{
|
|
Keys: bson.D{
|
|
{Key: "codeword", Value: 1},
|
|
{Key: "delete", Value: 1},
|
|
},
|
|
Options: options.Index().SetUnique(true).SetPartialFilterExpression(bson.M{"delete": false}),
|
|
}
|
|
_, err := mdb.Indexes().CreateOne(ctx, uniqueIndexModel)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
textIndexModel := mongo.IndexModel{
|
|
Keys: bson.D{
|
|
{Key: "codeword", Value: "text"},
|
|
{Key: "description", Value: "text"},
|
|
{Key: "greetings", Value: "text"},
|
|
},
|
|
Options: options.Index().SetName("TextIndex"),
|
|
}
|
|
_, err = mdb.Indexes().CreateOne(ctx, textIndexModel)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *PromoCodeRepository) CreatePromoCode(ctx context.Context, req *models.PromoCode) (*models.PromoCode, error) {
|
|
req.CreatedAt = time.Now()
|
|
req.ID = primitive.NewObjectID()
|
|
if req.FastLinks == nil {
|
|
req.FastLinks = []string{}
|
|
}
|
|
|
|
_, err := r.mdb.InsertOne(ctx, req)
|
|
if err != nil {
|
|
if writeErr, ok := err.(mongo.WriteException); ok {
|
|
for _, writeError := range writeErr.WriteErrors {
|
|
if writeError.Code == 11000 {
|
|
return nil, ErrDuplicateCodeword
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
|
|
return req, nil
|
|
}
|
|
|
|
func (r *PromoCodeRepository) EditPromoCode(ctx context.Context, req *models.ReqEditPromoCode) (*models.PromoCode, error) {
|
|
promoCodeID, err := primitive.ObjectIDFromHex(req.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
updateFields := bson.M{}
|
|
if req.Description != nil {
|
|
updateFields["description"] = *req.Description
|
|
}
|
|
if req.Greetings != nil {
|
|
updateFields["greetings"] = *req.Greetings
|
|
}
|
|
if req.DueTo != nil {
|
|
updateFields["dueTo"] = *req.DueTo
|
|
}
|
|
if req.ActivationCount != nil {
|
|
updateFields["activationCount"] = *req.ActivationCount
|
|
}
|
|
if req.Delete != nil {
|
|
updateFields["delete"] = *req.Delete
|
|
}
|
|
|
|
if req.Bonus != nil {
|
|
if req.Bonus.Privilege != nil {
|
|
if req.Bonus.Privilege.PrivilegeID != "" {
|
|
updateFields["bonus.privilege.privilegeID"] = req.Bonus.Privilege.PrivilegeID
|
|
}
|
|
if req.Bonus.Privilege.Amount != 0 {
|
|
updateFields["bonus.privilege.amount"] = req.Bonus.Privilege.Amount
|
|
}
|
|
}
|
|
if req.Bonus.Discount != nil {
|
|
if req.Bonus.Discount.Layer != 0 {
|
|
updateFields["bonus.discount.layer"] = req.Bonus.Discount.Layer
|
|
}
|
|
if req.Bonus.Discount.Factor != 0.0 {
|
|
updateFields["bonus.discount.factor"] = req.Bonus.Discount.Factor
|
|
}
|
|
if req.Bonus.Discount.Target != "" {
|
|
updateFields["bonus.discount.target"] = req.Bonus.Discount.Target
|
|
}
|
|
if req.Bonus.Discount.Threshold != 0 {
|
|
updateFields["bonus.discount.threshold"] = req.Bonus.Discount.Threshold
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(updateFields) == 0 {
|
|
return r.GetPromoCodeByID(ctx, promoCodeID)
|
|
}
|
|
|
|
update := bson.M{"$set": updateFields}
|
|
options := options.FindOneAndUpdate().SetReturnDocument(options.After)
|
|
result := r.mdb.FindOneAndUpdate(ctx, bson.M{"_id": promoCodeID}, update, options)
|
|
if result.Err() != nil {
|
|
return nil, result.Err()
|
|
}
|
|
|
|
var updatedPromoCode models.PromoCode
|
|
err = result.Decode(&updatedPromoCode)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &updatedPromoCode, nil
|
|
}
|
|
|
|
func (r *PromoCodeRepository) GetPromoCodeByID(ctx context.Context, promoCodeID primitive.ObjectID) (*models.PromoCode, error) {
|
|
var promoCode models.PromoCode
|
|
err := r.mdb.FindOne(ctx, bson.M{"_id": promoCodeID}).Decode(&promoCode)
|
|
if err != nil {
|
|
if err == mongo.ErrNoDocuments {
|
|
return nil, ErrPromoCodeNotFound
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
|
|
return &promoCode, nil
|
|
}
|
|
|
|
func (r *PromoCodeRepository) GetPromoCodesList(ctx context.Context, req *models.GetPromoCodesListReq) ([]models.PromoCode, int64, error) {
|
|
filter := bson.M{}
|
|
|
|
if req.Filter.Text != "" {
|
|
filter["$text"] = bson.M{"$search": req.Filter.Text}
|
|
}
|
|
|
|
if req.Filter.Active {
|
|
filter["delete"] = false
|
|
filter["outdated"] = false
|
|
filter["offLimit"] = false
|
|
} else {
|
|
filter["$or"] = []interface{}{
|
|
bson.M{"delete": true},
|
|
bson.M{"outdated": true},
|
|
bson.M{"offLimit": true},
|
|
}
|
|
}
|
|
|
|
opt := options.Find().SetSkip(int64(req.Page * req.Limit)).SetLimit(int64(req.Limit))
|
|
|
|
var countChan = make(chan countResult)
|
|
go func() {
|
|
defer close(countChan)
|
|
count, err := r.mdb.CountDocuments(ctx, filter)
|
|
countChan <- countResult{count, err}
|
|
}()
|
|
|
|
cursor, err := r.mdb.Find(ctx, filter, opt)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
defer cursor.Close(ctx)
|
|
|
|
var promoCodes = make([]models.PromoCode, 0, 10)
|
|
for cursor.Next(ctx) {
|
|
var p models.PromoCode
|
|
if err := cursor.Decode(&p); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
promoCodes = append(promoCodes, p)
|
|
}
|
|
|
|
if err := cursor.Err(); err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
result := <-countChan
|
|
if result.err != nil {
|
|
return nil, 0, result.err
|
|
}
|
|
count := result.count
|
|
|
|
return promoCodes, count, nil
|
|
}
|
|
|
|
func (r *PromoCodeRepository) ActivatePromo(ctx context.Context, req *models.ActivateReq) (*models.PromoCode, error) {
|
|
var promoCode models.PromoCode
|
|
|
|
var filter bson.M
|
|
if req.Codeword != "" {
|
|
filter = bson.M{
|
|
"codeword": req.Codeword,
|
|
}
|
|
} else if req.FastLink != "" {
|
|
filter = bson.M{
|
|
"fastLinks": req.FastLink,
|
|
}
|
|
}
|
|
|
|
opts := options.FindOneAndUpdate().SetReturnDocument(options.After)
|
|
|
|
err := r.mdb.FindOneAndUpdate(ctx, filter, bson.M{"$inc": bson.M{"activationCount": -1}}, opts).Decode(&promoCode)
|
|
if err != nil {
|
|
if err == mongo.ErrNoDocuments {
|
|
return nil, ErrPromoCodeNotFound
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
if promoCode.ActivationCount <= 0 && promoCode.DueTo > time.Now().Unix() {
|
|
if !promoCode.OffLimit {
|
|
update := bson.M{"$set": bson.M{"offLimit": true}}
|
|
_, err := r.mdb.UpdateOne(ctx, filter, update)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
return &promoCode, nil
|
|
}
|
|
|
|
func (r *PromoCodeRepository) IncreaseActivationCount(ctx context.Context, promoCodeID primitive.ObjectID) error {
|
|
filter := bson.M{"_id": promoCodeID}
|
|
update := bson.M{"$inc": bson.M{"activationCount": 1}}
|
|
_, err := r.mdb.UpdateOne(ctx, filter, update)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *PromoCodeRepository) DeletePromoCode(ctx context.Context, promoCodeID string) error {
|
|
id, err := primitive.ObjectIDFromHex(promoCodeID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
result, err := r.mdb.UpdateOne(
|
|
ctx,
|
|
bson.M{"_id": id, "delete": false},
|
|
bson.M{"$set": bson.M{"delete": true}},
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if result.MatchedCount == 0 {
|
|
return ErrPromoCodeNotFound
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *PromoCodeRepository) AddFastLink(ctx context.Context, promoCodeID primitive.ObjectID, xid string) error {
|
|
filter := bson.M{"_id": promoCodeID, "delete": false}
|
|
update := bson.M{"$push": bson.M{"fastLinks": xid}}
|
|
|
|
result, err := r.mdb.UpdateOne(ctx, filter, update)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if result.MatchedCount == 0 {
|
|
return ErrPromoCodeNotFound
|
|
}
|
|
|
|
return nil
|
|
}
|