simCardStatisticsRepo.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. package mongo
  2. import (
  3. "context"
  4. "fmt"
  5. "fwh-toolbox-server/database"
  6. "fwh-toolbox-server/domain"
  7. "time"
  8. "go.mongodb.org/mongo-driver/bson"
  9. "go.mongodb.org/mongo-driver/mongo"
  10. "go.mongodb.org/mongo-driver/mongo/options"
  11. )
  12. const (
  13. collectionSimCardRun = "SimCardDailyRun"
  14. collectionSimCardStatistics = "SimCardDailyStatistics"
  15. )
  16. func InitSimCardStatisticsIndexes(ctx context.Context) error {
  17. _, err := database.GetCollection(CollectionName_SimCardMonthlyData).Indexes().CreateMany(ctx, []mongo.IndexModel{
  18. {Keys: bson.D{{Key: "month", Value: 1}, {Key: "simId", Value: 1}}},
  19. {Keys: bson.D{{Key: "simId", Value: 1}, {Key: "month", Value: 1}}},
  20. })
  21. return err
  22. }
  23. type simCardRun struct {
  24. SmartHubLastPage *uint64 `bson:"smartHubLastPage"`
  25. AcceleronixLastPage *uint64 `bson:"acceleronixLastPage"`
  26. SavedPages []string `bson:"savedPages"`
  27. Sent bool `bson:"sent"`
  28. }
  29. func simCardPageKey(page domain.SimCardRunPage) string {
  30. return fmt.Sprintf("%s:%d", page.Platform, page.Page)
  31. }
  32. func MarkSimCardLastPage(date string, platform domain.SimCardPlatform, page uint64) error {
  33. field := "smartHubLastPage"
  34. if platform == domain.SimPlatformAcceleronix {
  35. field = "acceleronixLastPage"
  36. }
  37. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  38. defer cancel()
  39. _, err := database.GetCollection(collectionSimCardRun).UpdateOne(ctx, bson.M{"_id": date}, bson.M{"$set": bson.M{field: page}}, options.Update().SetUpsert(true))
  40. return err
  41. }
  42. func MarkSimCardPagesSaved(date string, pages []domain.SimCardRunPage) error {
  43. if len(pages) == 0 {
  44. return nil
  45. }
  46. keys := make([]string, 0, len(pages))
  47. for _, page := range pages {
  48. keys = append(keys, simCardPageKey(page))
  49. }
  50. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  51. defer cancel()
  52. _, err := database.GetCollection(collectionSimCardRun).UpdateOne(ctx, bson.M{"_id": date}, bson.M{"$addToSet": bson.M{"savedPages": bson.M{"$each": keys}}}, options.Update().SetUpsert(true))
  53. return err
  54. }
  55. func IsSimCardRunComplete(date string) (bool, error) {
  56. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  57. defer cancel()
  58. var run simCardRun
  59. err := database.GetCollection(collectionSimCardRun).FindOne(ctx, bson.M{"_id": date}).Decode(&run)
  60. if err == mongo.ErrNoDocuments {
  61. return false, nil
  62. }
  63. if err != nil {
  64. return false, err
  65. }
  66. return simCardRunComplete(run), nil
  67. }
  68. func simCardRunComplete(run simCardRun) bool {
  69. if run.Sent || run.SmartHubLastPage == nil || run.AcceleronixLastPage == nil {
  70. return false
  71. }
  72. saved := make(map[string]bool, len(run.SavedPages))
  73. for _, key := range run.SavedPages {
  74. saved[key] = true
  75. }
  76. for page := uint64(0); page <= *run.SmartHubLastPage; page++ {
  77. if !saved[simCardPageKey(domain.SimCardRunPage{Platform: domain.SimPlatformSmartHub, Page: page})] {
  78. return false
  79. }
  80. }
  81. for page := uint64(1); page <= *run.AcceleronixLastPage; page++ {
  82. if !saved[simCardPageKey(domain.SimCardRunPage{Platform: domain.SimPlatformAcceleronix, Page: page})] {
  83. return false
  84. }
  85. }
  86. return true
  87. }
  88. // StreamSimCardDailyUsage returns one snapshot per SIM for the UTC date.
  89. // Missing previous-day snapshots are represented by nil and excluded from increments.
  90. func StreamSimCardDailyUsage(date time.Time, consume func(total float64, previous *float64) error) error {
  91. ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
  92. defer cancel()
  93. month := date.Format("2006-01")
  94. day := date.Day()
  95. currentPath := fmt.Sprintf("$dailyUsage.%d.dataUsage", day)
  96. match := bson.D{{Key: "$match", Value: bson.M{
  97. "month": bson.M{"$in": []string{month, date.Format("200601")}},
  98. fmt.Sprintf("dailyUsage.%d", day): bson.M{"$exists": true},
  99. }}}
  100. pipeline := mongo.Pipeline{
  101. match,
  102. {{Key: "$sort", Value: bson.D{{Key: "updateTime", Value: -1}}}},
  103. {{Key: "$group", Value: bson.M{"_id": "$simId", "total": bson.M{"$first": currentPath}, "dailyUsage": bson.M{"$first": "$dailyUsage"}}}},
  104. }
  105. previousExpression := any(fmt.Sprintf("$dailyUsage.%d.dataUsage", day-1))
  106. if day == 1 {
  107. prev := time.Date(date.Year(), date.Month()-1, 1, 0, 0, 0, 0, time.UTC)
  108. prevDay := time.Date(date.Year(), date.Month(), 0, 0, 0, 0, 0, time.UTC).Day()
  109. pipeline = append(pipeline, bson.D{{Key: "$lookup", Value: bson.M{
  110. "from": CollectionName_SimCardMonthlyData,
  111. "let": bson.M{"id": "$_id"},
  112. "pipeline": mongo.Pipeline{
  113. {{Key: "$match", Value: bson.M{"month": bson.M{"$in": []string{prev.Format("2006-01"), prev.Format("200601")}}, "$expr": bson.M{"$eq": bson.A{"$simId", "$$id"}}}}},
  114. {{Key: "$limit", Value: 1}},
  115. },
  116. "as": "previousMonth",
  117. }}})
  118. previousExpression = bson.M{"$arrayElemAt": bson.A{fmt.Sprintf("$previousMonth.dailyUsage.%d.dataUsage", prevDay), 0}}
  119. }
  120. pipeline = append(pipeline, bson.D{{Key: "$project", Value: bson.M{"total": 1, "previous": previousExpression}}})
  121. cursor, err := database.GetCollection(CollectionName_SimCardMonthlyData).Aggregate(ctx, pipeline, options.Aggregate().SetAllowDiskUse(true))
  122. if err != nil {
  123. return err
  124. }
  125. defer cursor.Close(ctx)
  126. for cursor.Next(ctx) {
  127. var row struct {
  128. Total *float64 `bson:"total"`
  129. Previous *float64 `bson:"previous"`
  130. }
  131. if err := cursor.Decode(&row); err != nil {
  132. return err
  133. }
  134. if row.Total != nil {
  135. if err := consume(*row.Total, row.Previous); err != nil {
  136. return err
  137. }
  138. }
  139. }
  140. return cursor.Err()
  141. }
  142. func SaveSimCardDailyStatistics(stats domain.SimCardDailyStatistics) error {
  143. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  144. defer cancel()
  145. _, err := database.GetCollection(collectionSimCardStatistics).ReplaceOne(ctx, bson.M{"_id": stats.Date}, bson.M{"_id": stats.Date, "date": stats.Date, "cardCount": stats.CardCount, "totalOver200MB": stats.TotalOver200MB, "totalOver1GB": stats.TotalOver1GB, "totalOver2GB": stats.TotalOver2GB, "maxTotalKB": stats.MaxTotalKB, "incrementCardCount": stats.IncrementCardCount, "incrementOver20MB": stats.IncrementOver20MB, "incrementOver50MB": stats.IncrementOver50MB, "incrementOver200MB": stats.IncrementOver200MB, "maxIncrementKB": stats.MaxIncrementKB}, options.Replace().SetUpsert(true))
  146. return err
  147. }
  148. func MarkSimCardReportSent(date string) error {
  149. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  150. defer cancel()
  151. _, err := database.GetCollection(collectionSimCardRun).UpdateOne(ctx, bson.M{"_id": date}, bson.M{"$set": bson.M{"sent": true}})
  152. return err
  153. }