user_group_rate_resolver.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. package service
  2. import (
  3. "context"
  4. "fmt"
  5. "time"
  6. "github.com/Wei-Shaw/sub2api/internal/pkg/logger"
  7. gocache "github.com/patrickmn/go-cache"
  8. "golang.org/x/sync/singleflight"
  9. )
  10. type userGroupRateResolver struct {
  11. repo UserGroupRateRepository
  12. cache *gocache.Cache
  13. cacheTTL time.Duration
  14. sf *singleflight.Group
  15. logComponent string
  16. }
  17. func newUserGroupRateResolver(repo UserGroupRateRepository, cache *gocache.Cache, cacheTTL time.Duration, sf *singleflight.Group, logComponent string) *userGroupRateResolver {
  18. if cacheTTL <= 0 {
  19. cacheTTL = defaultUserGroupRateCacheTTL
  20. }
  21. if cache == nil {
  22. cache = gocache.New(cacheTTL, time.Minute)
  23. }
  24. if logComponent == "" {
  25. logComponent = "service.gateway"
  26. }
  27. if sf == nil {
  28. sf = &singleflight.Group{}
  29. }
  30. return &userGroupRateResolver{
  31. repo: repo,
  32. cache: cache,
  33. cacheTTL: cacheTTL,
  34. sf: sf,
  35. logComponent: logComponent,
  36. }
  37. }
  38. func (r *userGroupRateResolver) Resolve(ctx context.Context, userID, groupID int64, groupDefaultMultiplier float64) float64 {
  39. if r == nil || userID <= 0 || groupID <= 0 {
  40. return groupDefaultMultiplier
  41. }
  42. key := fmt.Sprintf("%d:%d", userID, groupID)
  43. if r.cache != nil {
  44. if cached, ok := r.cache.Get(key); ok {
  45. if multiplier, castOK := cached.(float64); castOK {
  46. userGroupRateCacheHitTotal.Add(1)
  47. return multiplier
  48. }
  49. }
  50. }
  51. if r.repo == nil {
  52. return groupDefaultMultiplier
  53. }
  54. userGroupRateCacheMissTotal.Add(1)
  55. value, err, shared := r.sf.Do(key, func() (any, error) {
  56. if r.cache != nil {
  57. if cached, ok := r.cache.Get(key); ok {
  58. if multiplier, castOK := cached.(float64); castOK {
  59. userGroupRateCacheHitTotal.Add(1)
  60. return multiplier, nil
  61. }
  62. }
  63. }
  64. userGroupRateCacheLoadTotal.Add(1)
  65. userRate, repoErr := r.repo.GetByUserAndGroup(ctx, userID, groupID)
  66. if repoErr != nil {
  67. return nil, repoErr
  68. }
  69. multiplier := groupDefaultMultiplier
  70. if userRate != nil {
  71. multiplier = *userRate
  72. }
  73. if r.cache != nil {
  74. r.cache.Set(key, multiplier, r.cacheTTL)
  75. }
  76. return multiplier, nil
  77. })
  78. if shared {
  79. userGroupRateCacheSFSharedTotal.Add(1)
  80. }
  81. if err != nil {
  82. userGroupRateCacheFallbackTotal.Add(1)
  83. logger.LegacyPrintf(r.logComponent, "get user group rate failed, fallback to group default: user=%d group=%d err=%v", userID, groupID, err)
  84. return groupDefaultMultiplier
  85. }
  86. multiplier, ok := value.(float64)
  87. if !ok {
  88. userGroupRateCacheFallbackTotal.Add(1)
  89. return groupDefaultMultiplier
  90. }
  91. return multiplier
  92. }