invite.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. package service
  2. import (
  3. "context"
  4. "crypto/md5"
  5. "encoding/hex"
  6. "fmt"
  7. "math/rand"
  8. "net"
  9. "sort"
  10. "sync"
  11. "time"
  12. "go-common/app/admin/main/usersuit/model"
  13. accmdl "go-common/app/service/main/account/api"
  14. "go-common/library/ecode"
  15. "go-common/library/log"
  16. "go-common/library/net/metadata"
  17. xtime "go-common/library/time"
  18. "go-common/library/sync/errgroup"
  19. )
  20. const (
  21. _geneMaxLimit = int64(1000)
  22. _geneSubCount = 200
  23. _batch = 50
  24. _fetchInfoTimeout = time.Second * 5
  25. )
  26. var (
  27. _emptyRichInvites = make([]*model.RichInvite, 0)
  28. _emptyInfoMap = make(map[int64]*accmdl.Info)
  29. )
  30. // Generate generate invite codes in batch.
  31. func (s *Service) Generate(c context.Context, mid, num, expireDay int64) (res []*model.RichInvite, err error) {
  32. if num > _geneMaxLimit {
  33. err = ecode.UsersuitInviteReachMaxGeneLimit
  34. return
  35. }
  36. expireSeconds := expireDay * 86400
  37. nowTs := time.Now().Unix()
  38. cm, err1 := concurrentGenerateCode(mid, nowTs, int(num), _geneSubCount)
  39. if err1 != nil {
  40. log.Error("concurrentGenerateCode(%d, %d, %d, %d) error(%v)", mid, nowTs, num, _geneSubCount, err)
  41. }
  42. ginvs := make([]*model.Invite, 0, num)
  43. buyIP := net.ParseIP(metadata.String(c, metadata.RemoteIP))
  44. for code := range cm {
  45. ginvs = append(ginvs, &model.Invite{
  46. Mid: mid,
  47. Code: code,
  48. IP: IPv4toN(buyIP),
  49. IPng: buyIP,
  50. Expires: nowTs + expireSeconds,
  51. Ctime: xtime.Time(nowTs),
  52. })
  53. }
  54. invs := make([]*model.Invite, 0)
  55. var rc int64
  56. for _, inv := range ginvs {
  57. if rc, err = s.d.AddIgnoreInvite(c, inv); err != nil {
  58. err = nil
  59. break
  60. }
  61. if rc == 0 {
  62. log.Error("service.dao.AddIgnoreInvite(%s), duplicate entry for invite_code %s", inv.Code, inv.Code)
  63. continue
  64. }
  65. invs = append(invs, inv)
  66. }
  67. res = s.fillStatusAndInviteeInfo(c, invs)
  68. return
  69. }
  70. func concurrentGenerateCode(mid, ts int64, num, subCount int) (res map[string]int, err error) {
  71. batches := num / subCount
  72. eg, _ := errgroup.WithContext(context.TODO())
  73. ims := make([]map[string]int, batches)
  74. mu := sync.Mutex{}
  75. for i := 0; i < batches; i++ {
  76. idx := i
  77. eg.Go(func() error {
  78. im := make(map[string]int)
  79. for len(im) < subCount {
  80. im[geneInviteCode(mid, ts)] = 1
  81. }
  82. mu.Lock()
  83. ims[idx] = im
  84. mu.Unlock()
  85. return nil
  86. })
  87. }
  88. err = eg.Wait()
  89. m := make(map[string]int)
  90. for _, im := range ims {
  91. for code := range im {
  92. m[code] = 1
  93. }
  94. }
  95. for len(m) < num {
  96. m[geneInviteCode(mid, ts)] = 1
  97. }
  98. res = m
  99. return
  100. }
  101. func geneInviteCode(mid int64, ts int64) string {
  102. data := md5.Sum([]byte(fmt.Sprintf("%d,%d,%d", ts, mid, rand.Int63())))
  103. h := hex.EncodeToString(data[:])
  104. return h[8:24]
  105. }
  106. // List list one's invite codes range time start and end.
  107. func (s *Service) List(c context.Context, mid, start, end int64) (res []*model.RichInvite, err error) {
  108. if start > end {
  109. res = _emptyRichInvites
  110. return
  111. }
  112. var invs []*model.Invite
  113. if invs, err = s.d.RangeInvites(c, mid, time.Unix(start, 0), time.Unix(end, 0)); err != nil {
  114. return
  115. }
  116. sort.Slice(invs, func(i, j int) bool {
  117. return int64(invs[i].Ctime) > int64(invs[j].Ctime)
  118. })
  119. res = s.fillStatusAndInviteeInfo(c, invs)
  120. return
  121. }
  122. func (s *Service) fillStatusAndInviteeInfo(c context.Context, invs []*model.Invite) []*model.RichInvite {
  123. if len(invs) == 0 {
  124. return _emptyRichInvites
  125. }
  126. imidm := make(map[int64]struct{})
  127. now := time.Now().Unix()
  128. for _, inv := range invs {
  129. inv.FillStatus(now)
  130. if inv.Status == model.StatusUsed {
  131. imidm[inv.Imid] = struct{}{}
  132. }
  133. }
  134. infom := _emptyInfoMap
  135. if len(imidm) > 0 {
  136. imids := make([]int64, 0, len(imidm))
  137. for imid := range imidm {
  138. imids = append(imids, imid)
  139. }
  140. var err1 error
  141. if infom, err1 = s.fetchInfos(c, imids, _fetchInfoTimeout); err1 != nil {
  142. log.Error("service.fetchInfos(%v, %s) error(%v)", imids, _fetchInfoTimeout, err1)
  143. }
  144. }
  145. rinvs := make([]*model.RichInvite, 0)
  146. for _, inv := range invs {
  147. rinvs = append(rinvs, model.NewRichInvite(inv, infom[inv.Imid]))
  148. }
  149. return rinvs
  150. }
  151. func (s *Service) fetchInfos(c context.Context, mids []int64, timeout time.Duration) (res map[int64]*accmdl.Info, err error) {
  152. if len(mids) == 0 {
  153. res = _emptyInfoMap
  154. return
  155. }
  156. batches := len(mids)/_batch + 1
  157. tc, cancel := context.WithTimeout(c, timeout)
  158. defer cancel()
  159. eg, errCtx := errgroup.WithContext(tc)
  160. bms := make([]map[int64]*accmdl.Info, batches)
  161. mu := sync.Mutex{}
  162. for i := 0; i < batches; i++ {
  163. idx := i
  164. end := (idx + 1) * _batch
  165. if idx == batches-1 {
  166. end = len(mids)
  167. }
  168. ids := mids[idx*_batch : end]
  169. eg.Go(func() error {
  170. m, err1 := s.accountClient.Infos3(errCtx, &accmdl.MidsReq{Mids: ids})
  171. mu.Lock()
  172. bms[idx] = m.Infos
  173. mu.Unlock()
  174. return err1
  175. })
  176. }
  177. err = eg.Wait()
  178. res = make(map[int64]*accmdl.Info)
  179. for _, bm := range bms {
  180. for mid, info := range bm {
  181. res[mid] = info
  182. }
  183. }
  184. return
  185. }
  186. // IPv4toN is
  187. func IPv4toN(ip net.IP) (sum uint32) {
  188. v4 := ip.To4()
  189. if v4 == nil {
  190. return
  191. }
  192. sum += uint32(v4[0]) << 24
  193. sum += uint32(v4[1]) << 16
  194. sum += uint32(v4[2]) << 8
  195. sum += uint32(v4[3])
  196. return sum
  197. }