|
@@ -2,12 +2,12 @@ package service
|
|
|
|
|
|
import (
|
|
import (
|
|
"context"
|
|
"context"
|
|
|
|
+ "math"
|
|
"time"
|
|
"time"
|
|
|
|
|
|
"github.com/mhaya/game/game_cluster/internal/mdb"
|
|
"github.com/mhaya/game/game_cluster/internal/mdb"
|
|
"github.com/mhaya/game/game_cluster/internal/mdb/models"
|
|
"github.com/mhaya/game/game_cluster/internal/mdb/models"
|
|
"github.com/mhaya/game/game_cluster/nodes/webadmin/entity"
|
|
"github.com/mhaya/game/game_cluster/nodes/webadmin/entity"
|
|
- "github.com/mhaya/game/game_cluster/nodes/webadmin/model"
|
|
|
|
"github.com/spf13/cast"
|
|
"github.com/spf13/cast"
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/mongo"
|
|
"go.mongodb.org/mongo-driver/mongo"
|
|
@@ -174,63 +174,86 @@ func (s *Synthesis) WithdrawalStatus(req *entity.UserWithdrawalStatus) error {
|
|
//
|
|
//
|
|
// 返回值为 UserCountryResp 的切片和错误。
|
|
// 返回值为 UserCountryResp 的切片和错误。
|
|
func (s *Synthesis) FindUserCountryCount() ([]*entity.UserCountryResp, error) {
|
|
func (s *Synthesis) FindUserCountryCount() ([]*entity.UserCountryResp, error) {
|
|
- // 设置超时的上下文
|
|
|
|
- ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
|
|
|
|
- defer cancel()
|
|
|
|
-
|
|
|
|
- // 获取数据库的集合
|
|
|
|
- collection := s.db.Collection("user_country_count")
|
|
|
|
-
|
|
|
|
- // 设置百分比阈值
|
|
|
|
- threshold := 1
|
|
|
|
|
|
+ // 选择数据库和集合
|
|
|
|
+ collection := mdb.MDB.Collection("playerCountryStat")
|
|
|
|
+
|
|
|
|
+ // 定义聚合管道
|
|
|
|
+ // 定义聚合管道
|
|
|
|
+ pipeline := []bson.D{
|
|
|
|
+ {
|
|
|
|
+ {Key: "$project", Value: bson.D{
|
|
|
|
+ {Key: "playerRegisterCountry", Value: bson.D{{Key: "$objectToArray", Value: "$playerRegisterCountry"}}},
|
|
|
|
+ }},
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ {Key: "$unwind", Value: "$playerRegisterCountry"},
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ {Key: "$group", Value: bson.D{
|
|
|
|
+ {Key: "_id", Value: "$playerRegisterCountry.k"},
|
|
|
|
+ {Key: "totalValue", Value: bson.D{{Key: "$sum", Value: "$playerRegisterCountry.v"}}},
|
|
|
|
+ }},
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ {Key: "$project", Value: bson.D{
|
|
|
|
+ {Key: "_id", Value: 0},
|
|
|
|
+ {Key: "countryKey", Value: "$_id"},
|
|
|
|
+ {Key: "totalValue", Value: 1},
|
|
|
|
+ }},
|
|
|
|
+ },
|
|
|
|
+ }
|
|
|
|
|
|
// 执行聚合查询
|
|
// 执行聚合查询
|
|
- var results []*entity.UserCountryResp
|
|
|
|
- cursor, err := collection.Find(ctx, bson.M{}, options.Find())
|
|
|
|
|
|
+ cursor, err := collection.Aggregate(context.TODO(), pipeline)
|
|
if err != nil {
|
|
if err != nil {
|
|
- return nil, err
|
|
|
|
|
|
+ panic(err)
|
|
}
|
|
}
|
|
- defer cursor.Close(ctx)
|
|
|
|
|
|
+ defer cursor.Close(context.TODO())
|
|
|
|
|
|
- // 解析结果
|
|
|
|
- for cursor.Next(ctx) {
|
|
|
|
- var result model.UserCountryCount
|
|
|
|
- if err := cursor.Decode(&result); err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- results = append(results, &entity.UserCountryResp{
|
|
|
|
- Country: result.Country,
|
|
|
|
- IPCount: result.IPCount,
|
|
|
|
- Percentage: result.Percentage,
|
|
|
|
- })
|
|
|
|
|
|
+ // 遍历查询结果
|
|
|
|
+ var results []bson.M
|
|
|
|
+ if err := cursor.All(context.TODO(), &results); err != nil {
|
|
|
|
+ panic(err)
|
|
}
|
|
}
|
|
-
|
|
|
|
- if err := cursor.Err(); err != nil {
|
|
|
|
- return nil, err
|
|
|
|
|
|
+ var totalIPCount int64
|
|
|
|
+ var data []*entity.UserCountryResp
|
|
|
|
+ // 将结果转换为 UserCountryResp
|
|
|
|
+ for _, r := range results {
|
|
|
|
+ var resp entity.UserCountryResp
|
|
|
|
+ resp.Country = r["countryKey"].(string)
|
|
|
|
+ resp.IPCount = int(r["totalValue"].(int32))
|
|
|
|
+ totalIPCount += int64(resp.IPCount)
|
|
|
|
+ data = append(data, &resp)
|
|
|
|
+ }
|
|
|
|
+ for _, v := range data {
|
|
|
|
+ // 保留小数点后两位
|
|
|
|
+ v.Percentage = math.Round(float64(v.IPCount)/float64(totalIPCount)*10000) / 100
|
|
}
|
|
}
|
|
|
|
|
|
// 根据阈值过滤结果
|
|
// 根据阈值过滤结果
|
|
otherCount := 0
|
|
otherCount := 0
|
|
- otherPercentage := 0
|
|
|
|
|
|
+ otherPercentage := 0.00
|
|
filteredResults := make([]*entity.UserCountryResp, 0)
|
|
filteredResults := make([]*entity.UserCountryResp, 0)
|
|
-
|
|
|
|
- for _, r := range results {
|
|
|
|
|
|
+ threshold := 1.00
|
|
|
|
+ for _, r := range data {
|
|
if r.Percentage >= threshold {
|
|
if r.Percentage >= threshold {
|
|
filteredResults = append(filteredResults, r)
|
|
filteredResults = append(filteredResults, r)
|
|
|
|
+ // 保留小数点后两位
|
|
|
|
+ r.Percentage = math.Round(r.Percentage*100) / 100
|
|
|
|
+ otherPercentage += r.Percentage
|
|
} else {
|
|
} else {
|
|
otherCount += r.IPCount
|
|
otherCount += r.IPCount
|
|
- otherPercentage += r.Percentage
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
// 将其他国家添加到过滤后的结果中
|
|
// 将其他国家添加到过滤后的结果中
|
|
if otherCount > 0 {
|
|
if otherCount > 0 {
|
|
|
|
+ p := 100.00 - math.Round(otherPercentage*100)/100
|
|
filteredResults = append(filteredResults, &entity.UserCountryResp{
|
|
filteredResults = append(filteredResults, &entity.UserCountryResp{
|
|
- Country: "other",
|
|
|
|
- IPCount: otherCount,
|
|
|
|
|
|
+ Country: "other",
|
|
|
|
+ IPCount: otherCount,
|
|
|
|
+ Percentage: math.Round(p*100) / 100,
|
|
})
|
|
})
|
|
}
|
|
}
|
|
-
|
|
|
|
return filteredResults, nil
|
|
return filteredResults, nil
|
|
|
|
|
|
}
|
|
}
|
|
@@ -241,44 +264,48 @@ func (s *Synthesis) FindUserCountryCount() ([]*entity.UserCountryResp, error) {
|
|
// 3. 计算留存率
|
|
// 3. 计算留存率
|
|
// 4. 返回结果
|
|
// 4. 返回结果
|
|
func (s *Synthesis) FindUserRetention(req *entity.UserRetentionReq) ([]*entity.UserRetentionResp, error) {
|
|
func (s *Synthesis) FindUserRetention(req *entity.UserRetentionReq) ([]*entity.UserRetentionResp, error) {
|
|
- ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
|
|
|
|
- defer cancel()
|
|
|
|
- collection := s.db.Collection("user_reg_count")
|
|
|
|
-
|
|
|
|
- // 构建过滤器
|
|
|
|
- filter := bson.M{}
|
|
|
|
- if req.StartTime != 0 {
|
|
|
|
- filter["registration_date"] = bson.M{
|
|
|
|
- "$gte": req.StartTime,
|
|
|
|
- "$lte": req.EndTime,
|
|
|
|
- } // 日期范围
|
|
|
|
- }
|
|
|
|
- // 设置分页选项
|
|
|
|
- findOptions := options.Find()
|
|
|
|
- findOptions.SetSkip(int64((req.Page - 1) * req.Size))
|
|
|
|
- findOptions.SetLimit(int64(req.Size))
|
|
|
|
-
|
|
|
|
|
|
+ playerPreserve := models.GetPlayerPreserve(req.StartTime, req.EndTime)
|
|
// 查询数据
|
|
// 查询数据
|
|
var results []*entity.UserRetentionResp
|
|
var results []*entity.UserRetentionResp
|
|
- cursor, err := collection.Find(ctx, filter, findOptions)
|
|
|
|
- if err != nil {
|
|
|
|
- return nil, err
|
|
|
|
- }
|
|
|
|
- defer cursor.Close(ctx)
|
|
|
|
|
|
|
|
- // 解析结果
|
|
|
|
- for cursor.Next(ctx) {
|
|
|
|
- var result model.UserRetentionData
|
|
|
|
- if err := cursor.Decode(&result); err != nil {
|
|
|
|
- return nil, err
|
|
|
|
|
|
+ for key, v := range playerPreserve {
|
|
|
|
+ var resp entity.UserRetentionResp
|
|
|
|
+ var retention entity.Retention
|
|
|
|
+ for _, vv := range v {
|
|
|
|
+ if vv.ID == 1 {
|
|
|
|
+ retention.Day1 = entity.DayRetention{
|
|
|
|
+ LoggedIn: vv.Ratio,
|
|
|
|
+ LoginDate: key,
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if vv.ID == 3 {
|
|
|
|
+ retention.Day3 = entity.DayRetention{
|
|
|
|
+ LoggedIn: vv.Ratio,
|
|
|
|
+ LoginDate: key,
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if vv.ID == 7 {
|
|
|
|
+ retention.Day7 = entity.DayRetention{
|
|
|
|
+ LoggedIn: vv.Ratio,
|
|
|
|
+ LoginDate: key,
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if vv.ID == 14 {
|
|
|
|
+ retention.Day14 = entity.DayRetention{
|
|
|
|
+ LoggedIn: vv.Ratio,
|
|
|
|
+ LoginDate: key,
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if vv.ID == 30 {
|
|
|
|
+ retention.Day30 = entity.DayRetention{
|
|
|
|
+ LoggedIn: vv.Ratio,
|
|
|
|
+ LoginDate: key,
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- results = append(results, &entity.UserRetentionResp{
|
|
|
|
- UserRetentionData: &result,
|
|
|
|
- })
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if err := cursor.Err(); err != nil {
|
|
|
|
- return nil, err
|
|
|
|
|
|
+ resp.RegistrationDate = key
|
|
|
|
+ resp.RetentionData = retention
|
|
|
|
+ results = append(results, &resp)
|
|
}
|
|
}
|
|
|
|
|
|
return results, nil
|
|
return results, nil
|