From 7d3bb7c675e7b55cce71219646e518a27739b539 Mon Sep 17 00:00:00 2001 From: Pavel Date: Wed, 24 Apr 2024 15:20:21 +0300 Subject: [PATCH 1/4] refactor with new ag query --- api/openapi/v1/openapi.yaml | 46 +++++---- internal/interface/repository/account.go | 125 +++++++++++++++++------ internal/interface/swagger/api.2.go | 52 +--------- tests/integration/logostat_test.go | 2 +- 4 files changed, 122 insertions(+), 103 deletions(-) diff --git a/api/openapi/v1/openapi.yaml b/api/openapi/v1/openapi.yaml index 5d57d06..4de2da3 100644 --- a/api/openapi/v1/openapi.yaml +++ b/api/openapi/v1/openapi.yaml @@ -975,29 +975,33 @@ components: type: string example: user not found QuizLogoStat: - type: object + type: array properties: - count: +# count: +# type: integer +# description: Общее количество + _id: + type: string + description: user id + money: type: integer - description: Общее количество - items: - type: object - additionalProperties: - type: object - properties: - money: - type: integer - description: Количество денег - regs: - type: integer - description: Количество регистраций - quizes: - type: object - additionalProperties: - type: array - items: - type: integer - description: Массив значений для каждого квиза 1 элемент регистрации по квизу, второй количество денег по квизу + description: Количество денег + regs: + type: integer + description: Количество регистраций + quizes: + type: array + properties: + quiz: + type: string + description: qid quiz + regs: + type: integer + description: Количество регистраций + money: + type: integer + description: Количество денег + securitySchemes: Bearer: # arbitrary name for the security scheme diff --git a/internal/interface/repository/account.go b/internal/interface/repository/account.go index b3c7301..8386235 100644 --- a/internal/interface/repository/account.go +++ b/internal/interface/repository/account.go @@ -410,47 +410,106 @@ type QuizLogoStatDeps struct { To *int } -func (receiver *AccountRepository) QuizLogoStat(ctx context.Context, req QuizLogoStatDeps) ([]models.Account, int64, error) { - filter := bson.M{ - "from": bson.M{"$ne":"","$exists":true}, - "partner": bson.M{"$ne":"","$exists":true}, - } - filter2 := bson.M{ - "from": bson.M{"$ne":"","$exists":true}, - "partner": bson.M{"$ne":"","$exists":true}, - } - if req.From != nil || req.To != nil { - timeRange := bson.M{} - if *req.From != 0 { - timeRange["$gte"] = time.Unix(int64(*req.From), 0) +type QuizLogoStats struct { + ID string `bson:"_id"` + Regs int `bson:"regs"` + Money int64 `bson:"money"` + Quizes []Quiz `bson:"quizes"` +} + +type Quiz struct { + QuizID string `bson:"quiz"` + Regs int `bson:"regs"` + Money int64 `bson:"money"` +} + +func (receiver *AccountRepository) QuizLogoStat(ctx context.Context, req QuizLogoStatDeps) ([]QuizLogoStats, error) { + var pipeline mongo.Pipeline + + if req.From != nil && req.To != nil { + match := bson.D{ + {"$match", bson.D{ + {"createdAt", bson.D{{"$gte", time.Unix(int64(*req.From), 0)}}}, + {"createdAt", bson.D{{"$lte", time.Unix(int64(*req.To), 0)}}}, + {"from", bson.D{{"$exists", true}}}, + {"partner", bson.D{{"$exists", true}}}, + }}, } - if *req.To != 0 { - timeRange["$lte"] = time.Unix(int64(*req.To), 0) + pipeline = append(pipeline, match) + } else { + match := bson.D{ + {"$match", bson.D{ + {"from", bson.D{{"$exists", true}}}, + {"partner", bson.D{{"$exists", true}}}, + }}, } - filter["createdAt"] = timeRange + pipeline = append(pipeline, match) } - options := options.Find() - if req.Page != nil && req.Limit != nil { - options.SetSkip(int64(*req.Page * *req.Limit)) - options.SetLimit(int64(*req.Limit)) - } + pipeline = append(pipeline, mongo.Pipeline{ + { + {"$lookup", bson.D{ + {"from", "histories"}, + {"localField", "userId"}, + {"foreignField", "userId"}, + {"as", "history"}, + }}, + }, + { + {"$unwind", bson.D{ + {"path", "$history"}, + {"preserveNullAndEmptyArrays", true}, + }}, + }, + { + {"$match", bson.D{ + {"$or", bson.A{ + bson.D{{"history", bson.D{{"$exists", false}}}}, + bson.D{{"history.key", "payment.succeeded"}}, + }}, + }}, + }, + { + {"$group", bson.D{ + {"_id", "$userId"}, + {"partner", bson.D{{"$first", "$partner"}}}, + {"from", bson.D{{"$first", "$from"}}}, + {"sum", bson.D{{"$sum", "$history.rawDetails.price"}}}, + }}, + }, + { + {"$group", bson.D{ + {"_id", "$from"}, + {"partner", bson.D{{"$first", "$partner"}}}, + {"regs", bson.D{{"$count", bson.D{}}}}, + {"money", bson.D{{"$sum", "$sum"}}}, + }}, + }, + { + {"$group", bson.D{ + {"_id", "$partner"}, + {"regs", bson.D{{"$sum", "$regs"}}}, + {"money", bson.D{{"$sum", "$money"}}}, + {"quizes", bson.D{{"$push", bson.D{ + {"quiz", "$_id"}, + {"regs", "$regs"}, + {"money", "$money"}, + }}}}}, + }, + }, + }...) - count, err := receiver.mongoDB.CountDocuments(ctx, filter2) + var results []QuizLogoStats + + cursor, err := receiver.mongoDB.Aggregate(ctx, pipeline) if err != nil { - return nil, 0, err - } - fmt.Println("ACCCCCCCCCCCC", filter,options, *req.Limit) - - cursor, err := receiver.mongoDB.Find(ctx, filter) - if err != nil { - return nil, 0, err + return nil, err } defer cursor.Close(ctx) - var accounts []models.Account - if err := cursor.All(ctx, &accounts); err != nil { - return nil, 0, err + + if err := cursor.All(ctx, &results); err != nil { + return nil, err } - return accounts, count, nil + return results, nil } diff --git a/internal/interface/swagger/api.2.go b/internal/interface/swagger/api.2.go index 5fb24aa..3027976 100644 --- a/internal/interface/swagger/api.2.go +++ b/internal/interface/swagger/api.2.go @@ -63,7 +63,7 @@ func NewAPI2(logger *zap.Logger, db *mongo.Database, config *models.Config, cons account: repository.NewAccountRepository2(logger, db.Collection("accounts")), consumer: consumer, producer: producer, - encrypt: encrypt, + encrypt: encrypt, grpc: config.GRPC, clients: clients{ auth: client.NewAuthClient(client.AuthClientDeps{Logger: logger, URLs: &config.Service.AuthMicroservice.URL}), @@ -874,59 +874,15 @@ func (api *API2) QuizLogoStat(ctx echo.Context) error { return api.error(ctx, http.StatusBadRequest, "failed to bind request") } - //получаем аккаунты с непустыми quizfrom и partner, с пагинацией за заданный период - accounts, count, err := api.account.QuizLogoStat(ctx.Request().Context(), repository.QuizLogoStatDeps{ + result, err := api.account.QuizLogoStat(ctx.Request().Context(), repository.QuizLogoStatDeps{ Page: req.Page, Limit: req.Limit, From: req.From, To: req.To, }) if err != nil { - return api.error(ctx, http.StatusInternalServerError, "failed getting accounts in period with pagination") + return api.error(ctx, http.StatusInternalServerError, fmt.Sprint("failed getting quiz logo stat", err.Error())) } - historyMap, err := api.history.GetHistoryByListUsers(ctx.Request().Context(), accounts) - // partner тот кто позвал quizfrom qid квиза нужно посчитать первое количество регистраций и денег у это партнера и тоже самое по квизу этого партнера - resp := QuizLogoStat2{ - Count: count, - Items: make(map[string]Item), - } - - fmt.Println("AC", accounts) - for _, account := range accounts { - partner := account.Partner - quizFrom := account.From - fmt.Println("ACC", account) - // проверка на то что существует ли такой партнер в мапе - if _, exists := resp.Items[partner]; !exists { - resp.Items[partner] = Item{ - Money: 0, - Quizes: make(map[string][2]int), - Regs: 0, - } - } - // тут аналогичная проверка на Quizes - if _, exists := resp.Items[partner].Quizes[quizFrom]; !exists { - resp.Items[partner].Quizes[quizFrom] = [2]int{0, 0} - } - - // если все ок то начинаем получать историю - historys := historyMap[account.UserID] - // ренжим историю заполняем resp прайс из RawDetails также инкрементируем значения регистраций - currentItem := resp.Items[partner] - currentItem.Regs++ - fmt.Println("ACCC", currentItem, resp) - for _, history := range historys { - currentItem.Money += history.RawDetails.Price - currentQuizes := currentItem.Quizes[quizFrom] - // первый элемент это регистрации по квизу - currentQuizes[0] = currentQuizes[0] + 1 - // второй элемент прайс по квизу - currentQuizes[1] = currentQuizes[1] + int(history.RawDetails.Price) - currentItem.Quizes[quizFrom] = currentQuizes - } - resp.Items[partner] = currentItem - } - - return ctx.JSON(http.StatusOK, resp) + return ctx.JSON(http.StatusOK, result) } diff --git a/tests/integration/logostat_test.go b/tests/integration/logostat_test.go index c7d6be8..97c45c3 100644 --- a/tests/integration/logostat_test.go +++ b/tests/integration/logostat_test.go @@ -26,7 +26,7 @@ func TestLogostat(t *testing.T) { mongoDB, err := mongo.Connect(ctx, &mongo.ConnectDeps{ Configuration: &mongo.Configuration{ Host: "localhost", - Port: "27024", + Port: "27020", User: "test", Password: "test", Auth: "admin", From d3ae92feec8f058a1ad319d457b1fe1bda45823f Mon Sep 17 00:00:00 2001 From: Pavel Date: Wed, 24 Apr 2024 15:27:17 +0300 Subject: [PATCH 2/4] update openapi --- api/openapi/v1/openapi.yaml | 55 ++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/api/openapi/v1/openapi.yaml b/api/openapi/v1/openapi.yaml index 4de2da3..901b857 100644 --- a/api/openapi/v1/openapi.yaml +++ b/api/openapi/v1/openapi.yaml @@ -976,31 +976,36 @@ components: example: user not found QuizLogoStat: type: array - properties: -# count: -# type: integer -# description: Общее количество - _id: - type: string - description: user id - money: - type: integer - description: Количество денег - regs: - type: integer - description: Количество регистраций - quizes: - type: array - properties: - quiz: - type: string - description: qid quiz - regs: - type: integer - description: Количество регистраций - money: - type: integer - description: Количество денег + items: + # count: + # type: integer + # description: Общее количество + type: object + properties: + _id: + type: string + description: user id + money: + type: integer + description: Количество денег + regs: + type: integer + description: Количество регистраций + quizes: + type: array + items: + type: object + properties: + quiz: + type: string + description: qid quiz + regs: + type: integer + description: Количество регистраций + money: + type: integer + description: Количество денег + securitySchemes: From 953a7c569e23886edc168a1bdd87686f3212c82b Mon Sep 17 00:00:00 2001 From: Pavel Date: Wed, 24 Apr 2024 15:29:07 +0300 Subject: [PATCH 3/4] update openapi --- api/openapi/v1/openapi.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/openapi/v1/openapi.yaml b/api/openapi/v1/openapi.yaml index 901b857..9143e09 100644 --- a/api/openapi/v1/openapi.yaml +++ b/api/openapi/v1/openapi.yaml @@ -982,7 +982,7 @@ components: # description: Общее количество type: object properties: - _id: + id: type: string description: user id money: From ef5fc30f0a4cb1c24febe9035867b17ddb78b0f4 Mon Sep 17 00:00:00 2001 From: Pavel Date: Wed, 24 Apr 2024 15:35:55 +0300 Subject: [PATCH 4/4] add ne after testing --- internal/interface/repository/account.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/interface/repository/account.go b/internal/interface/repository/account.go index 8386235..75142cb 100644 --- a/internal/interface/repository/account.go +++ b/internal/interface/repository/account.go @@ -431,16 +431,16 @@ func (receiver *AccountRepository) QuizLogoStat(ctx context.Context, req QuizLog {"$match", bson.D{ {"createdAt", bson.D{{"$gte", time.Unix(int64(*req.From), 0)}}}, {"createdAt", bson.D{{"$lte", time.Unix(int64(*req.To), 0)}}}, - {"from", bson.D{{"$exists", true}}}, - {"partner", bson.D{{"$exists", true}}}, + {"from", bson.D{{"$exists", true}, {"$ne", ""}}}, + {"partner", bson.D{{"$exists", true}, {"$ne", ""}}}, }}, } pipeline = append(pipeline, match) } else { match := bson.D{ {"$match", bson.D{ - {"from", bson.D{{"$exists", true}}}, - {"partner", bson.D{{"$exists", true}}}, + {"from", bson.D{{"$exists", true}, {"$ne", ""}}}, + {"partner", bson.D{{"$exists", true}, {"$ne", ""}}}, }}, } pipeline = append(pipeline, match)