package cachecurd import ( "context" "joybuywork/internal/app" "joybuywork/internal/repository/dao" "joybuywork/internal/repository/model" "joybuywork/internal/repository/mtype" "joybuywork/internal/utils/lockdo" "fmt" "time" grds "github.com/go-redis/redis/v8" "gorm.io/gen" ) type RankOption func(rank *Ranker) func SetChildBuild(cpnId int64, childType int8) RankOption { return func(rank *Ranker) { rank.build = &RankBuild{ CpnId: cpnId, ChildType: childType, } } } func SetRankExp(exp time.Duration) RankOption { return func(rank *Ranker) { rank.exp = exp } } type Ranker struct { ctx context.Context cli *grds.Client key string build *RankBuild exp time.Duration } type RankBuild struct { CpnId int64 ChildType int8 } func NewRanker(ctx context.Context, key string, opts ...RankOption) (*Ranker, error) { rank := &Ranker{ ctx: ctx, cli: app.Redis.Cache, key: key, exp: time.Hour * 24, } for _, opt := range opts { opt(rank) } err := rank.Build() if err != nil { return nil, err } return rank, nil } func (s *Ranker) Build() (err error) { if s.build == nil { return } if s.build.ChildType == mtype.FissionCampaignChildTypeInvite { return s.invite(s.build) } return } type RankMember struct { UserId string Score float64 } // GetRank 获取排名数据 func (s *Ranker) GetRank(num int64) (members []*RankMember, err error) { data, err := s.cli.ZRevRangeWithScores(s.ctx, s.key, 0, num-1).Result() if err != nil { return } members = make([]*RankMember, len(data)) for i, v := range data { members[i] = &RankMember{ UserId: fmt.Sprintf("%v", v.Member), Score: v.Score, } } return } // InitMember 初始化一个用户 func (s *Ranker) InitMember(member string, asc int64) (err error) { return s.SetMembers([]*grds.Z{{ Score: 1 + s.timeAsc(asc), Member: member, }}) } // SetMembers 设置用户 func (s *Ranker) SetMembers(members []*grds.Z) (err error) { return s.cli.ZAdd(s.ctx, s.key, members...).Err() } // GetMemberRank 获取用户当前排名,如果返回9,是第10名的意思 func (s *Ranker) GetMemberRank(member string) (rank int64, err error) { return s.cli.ZRevRank(s.ctx, s.key, member).Result() } // Incr 追加分数 func (s *Ranker) Incr(member string) (err error) { return s.IncrBy(1, member) } // IncrBy 追加特定分数 func (s *Ranker) IncrBy(increment float64, member string) (err error) { return s.cli.ZIncrBy(s.ctx, s.key, increment, member).Err() } // 子活动排行榜初始化 func (s *Ranker) invite(build *RankBuild) (err error) { return lockdo.ExistDo(s.ctx, s.key, func(exist bool) (err error) { if exist { return } m := dao.FissionChildUser var list []*model.FissionChildUser err = m.WithContext(s.ctx).Where(m.CampaignID.Eq(build.CpnId), m.InviteNum.Gt(0)).FindInBatches(&list, 200, func(tx gen.Dao, batch int) error { var members = make([]*grds.Z, 0, len(list)) for _, v := range list { members = append(members, &grds.Z{ Score: float64(v.InviteNum) + s.timeAsc(v.CreatedAt), Member: fmt.Sprint(v.UserID), }) } return s.SetMembers(members) }) s.setExp() return }) } func (s *Ranker) setExp() { s.cli.Expire(s.ctx, s.key, s.exp) } func (s *Ranker) timeAsc(asc int64) float64 { return 1 - float64(asc)/1e13 }