package repository import ( "codeword/internal/models" "context" "errors" "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" ) var ( ErrDuplicateCodeword = errors.New("duplicate codeword") ErrPromoCodeNotFound = errors.New("promo code not found") ) // структура для горутины чтобы ошибки не пропускать type countResult struct { count int64 err error } type PromoCodeRepository struct { mdb *mongo.Collection } func NewPromoCodeRepository(mdb *mongo.Collection) *PromoCodeRepository { // todo заменить паники вроде как в роде не круто их юзать 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(context.Background(), uniqueIndexModel) if err != nil { panic(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(context.Background(), textIndexModel) if err != nil { panic(err) } return &PromoCodeRepository{mdb: mdb} } func (r *PromoCodeRepository) CreatePromoCode(ctx context.Context, req *models.PromoCode) (*models.PromoCode, error) { req.CreatedAt = time.Now() req.ID = primitive.NewObjectID() _, 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 len(updateFields) == 0 { return r.GetPromoCodeByID(ctx, promoCodeID) } update := bson.M{"$set": updateFields} result, err := r.mdb.UpdateOne(ctx, bson.M{"_id": promoCodeID}, update) if err != nil { return nil, err } if result.MatchedCount == 0 { return nil, ErrPromoCodeNotFound } return r.GetPromoCodeByID(ctx, promoCodeID) } 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) 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 }