invite.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. package usersuit
  2. import (
  3. "context"
  4. "sync"
  5. "time"
  6. "go-common/app/interface/main/account/model"
  7. accmdl "go-common/app/service/main/account/model"
  8. usmdl "go-common/app/service/main/usersuit/model"
  9. "go-common/library/log"
  10. "go-common/library/net/metadata"
  11. "go-common/library/sync/errgroup"
  12. )
  13. const (
  14. _batch = 20
  15. _fetchInfoTimeout = time.Second * 10
  16. )
  17. var (
  18. _emptyRichInvites = make([]*model.RichInvite, 0)
  19. _emptyInfoMap = make(map[int64]*accmdl.Info)
  20. )
  21. // Buy buy invite code.
  22. func (s *Service) Buy(c context.Context, mid int64, num int64) (res []*model.RichInvite, err error) {
  23. var invs []*usmdl.Invite
  24. ip := metadata.String(c, metadata.RemoteIP)
  25. arg := &usmdl.ArgBuy{Mid: mid, Num: num, IP: ip}
  26. if invs, err = s.usRPC.Buy(c, arg); err != nil {
  27. log.Error("service.userserviceRPC.Buy(%v) error(%v)", arg, err)
  28. return
  29. }
  30. res = make([]*model.RichInvite, 0)
  31. for _, inv := range invs {
  32. res = append(res, model.NewRichInvite(inv, nil))
  33. }
  34. return
  35. }
  36. // Apply apply invite code.
  37. func (s *Service) Apply(c context.Context, mid int64, code string, cookie string) (err error) {
  38. ip := metadata.String(c, metadata.RemoteIP)
  39. arg := &usmdl.ArgApply{Mid: mid, Code: code, Cookie: cookie, IP: ip}
  40. if err = s.usRPC.Apply(c, arg); err != nil {
  41. log.Error("service.userserviceRPC.Apply(%v) error(%v)", arg, err)
  42. }
  43. return
  44. }
  45. // Stat get user's invite code stat.
  46. func (s *Service) Stat(c context.Context, mid int64) (res *model.RichInviteStat, err error) {
  47. var st *usmdl.InviteStat
  48. ip := metadata.String(c, metadata.RemoteIP)
  49. arg := &usmdl.ArgStat{Mid: mid, IP: ip}
  50. if st, err = s.usRPC.Stat(c, arg); err != nil {
  51. log.Error("service.userserviceRPC.Stat(%v) error(%v)", arg, err)
  52. return
  53. }
  54. res = &model.RichInviteStat{
  55. Mid: st.Mid,
  56. CurrentLimit: st.CurrentLimit,
  57. CurrentBought: st.CurrentBought,
  58. TotalBought: st.TotalBought,
  59. TotalUsed: st.TotalUsed,
  60. InviteCodes: s.fillInviteeInfo(c, st.InviteCodes, ip),
  61. }
  62. return
  63. }
  64. func (s *Service) fillInviteeInfo(c context.Context, invs []*usmdl.Invite, ip string) []*model.RichInvite {
  65. if len(invs) == 0 {
  66. return _emptyRichInvites
  67. }
  68. imidm := make(map[int64]int)
  69. for _, inv := range invs {
  70. if inv.Status == usmdl.StatusUsed {
  71. imidm[inv.Imid] = 1
  72. }
  73. }
  74. infom := _emptyInfoMap
  75. if len(imidm) > 0 {
  76. imids := make([]int64, 0, len(imidm))
  77. for imid := range imidm {
  78. imids = append(imids, imid)
  79. }
  80. var err1 error
  81. if infom, err1 = s.fetchInfos(c, imids, ip, _fetchInfoTimeout); err1 != nil {
  82. log.Error("service.fetchInfos(%v, %s, %v) error(%v)", imids, ip, _fetchInfoTimeout, err1)
  83. }
  84. }
  85. rinvs := make([]*model.RichInvite, 0)
  86. for _, inv := range invs {
  87. rinvs = append(rinvs, model.NewRichInvite(inv, infom[inv.Imid]))
  88. }
  89. return rinvs
  90. }
  91. func (s *Service) fetchInfos(c context.Context, mids []int64, ip string, timeout time.Duration) (res map[int64]*accmdl.Info, err error) {
  92. if len(mids) == 0 {
  93. res = _emptyInfoMap
  94. return
  95. }
  96. batches := len(mids)/_batch + 1
  97. tc, cancel := context.WithTimeout(c, timeout)
  98. defer cancel()
  99. eg, errCtx := errgroup.WithContext(tc)
  100. bms := make([]map[int64]*accmdl.Info, batches)
  101. mu := sync.Mutex{}
  102. for i := 0; i < batches; i++ {
  103. idx := i
  104. end := (idx + 1) * _batch
  105. if idx == batches-1 {
  106. end = len(mids)
  107. }
  108. ids := mids[idx*_batch : end]
  109. eg.Go(func() error {
  110. m, err1 := s.accRPC.Infos3(errCtx, &accmdl.ArgMids{Mids: ids})
  111. mu.Lock()
  112. bms[idx] = m
  113. mu.Unlock()
  114. return err1
  115. })
  116. }
  117. err = eg.Wait()
  118. res = make(map[int64]*accmdl.Info)
  119. for _, bm := range bms {
  120. for mid, info := range bm {
  121. res[mid] = info
  122. }
  123. }
  124. return
  125. }