You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
353 lines
11 KiB
353 lines
11 KiB
|
3 months ago
|
package call
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"github.com/liangdas/mqant/log"
|
||
|
|
"gorm.io/gorm"
|
||
|
|
"gorm.io/gorm/clause"
|
||
|
|
"server/common"
|
||
|
|
"server/db"
|
||
|
|
"server/util"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
var rankTimerMap map[string]*rankTicker
|
||
|
|
|
||
|
|
type rankTicker struct {
|
||
|
|
timer *time.Ticker
|
||
|
|
ctx context.Context
|
||
|
|
ctxCancel context.CancelFunc
|
||
|
|
|
||
|
|
rankType int
|
||
|
|
rankCycle string
|
||
|
|
activityId int
|
||
|
|
}
|
||
|
|
|
||
|
|
func getRankTimerKey(rankType int, rankCycle string) string {
|
||
|
|
return fmt.Sprintf("rank|%d|%s", rankType, rankCycle)
|
||
|
|
}
|
||
|
|
|
||
|
|
func getRankJackpotKey(rankType int, rankCycle string) (rankAt, awardAt time.Time, expired int64, key string) {
|
||
|
|
now := time.Now()
|
||
|
|
key = fmt.Sprintf("rank_jackpot|%d|%s", rankType, rankCycle)
|
||
|
|
switch rankCycle {
|
||
|
|
case "1":
|
||
|
|
rankAt = util.GetZeroTime(now)
|
||
|
|
awardAt = rankAt.AddDate(0, 0, 1)
|
||
|
|
expired = int64(rankAt.AddDate(0, 0, 2).Sub(now).Seconds())
|
||
|
|
case "2":
|
||
|
|
rankAt = util.GetWeekZeroTime(now)
|
||
|
|
awardAt = rankAt.AddDate(0, 0, 7)
|
||
|
|
expired = int64(rankAt.AddDate(0, 0, 14).Sub(now).Seconds())
|
||
|
|
case "3":
|
||
|
|
rankAt = util.GetFirstDateOfMonth(now)
|
||
|
|
awardAt = rankAt.AddDate(0, 1, 0)
|
||
|
|
expired = int64(rankAt.AddDate(0, 2, 0).Sub(now).Seconds())
|
||
|
|
}
|
||
|
|
key += fmt.Sprintf("|%s", rankAt.Format("20060102"))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
func getRankJackpotPreKey(rankType int, rankCycle string) (lastRankAt time.Time, key string) {
|
||
|
|
now := time.Now()
|
||
|
|
key = fmt.Sprintf("rank_jackpot|%d|%s", rankType, rankCycle)
|
||
|
|
switch rankCycle {
|
||
|
|
case "1":
|
||
|
|
lastRankAt = util.GetZeroTime(now).AddDate(0, 0, -1)
|
||
|
|
case "2":
|
||
|
|
lastRankAt = util.GetWeekZeroTime(now).AddDate(0, 0, -7)
|
||
|
|
case "3":
|
||
|
|
lastRankAt = util.GetFirstDateOfMonth(now).AddDate(0, -1, 0)
|
||
|
|
}
|
||
|
|
key += fmt.Sprintf("|%s", lastRankAt.Format("20060102"))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// RankHandler 加载排行榜发奖程序
|
||
|
|
func RankHandler() {
|
||
|
|
configRanks := GetConfigRank(0)
|
||
|
|
if len(configRanks) == 0 {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
now := time.Now()
|
||
|
|
if rankTimerMap == nil {
|
||
|
|
rankTimerMap = make(map[string]*rankTicker)
|
||
|
|
}
|
||
|
|
for _, rank := range configRanks {
|
||
|
|
var (
|
||
|
|
rankType = rank.RankType
|
||
|
|
awardAt time.Time
|
||
|
|
)
|
||
|
|
for rankCycle := range rank.RankCycleMap {
|
||
|
|
switch rankCycle {
|
||
|
|
case "1":
|
||
|
|
awardAt = util.GetZeroTime(now.AddDate(0, 0, 1)).Add(time.Hour)
|
||
|
|
case "2":
|
||
|
|
awardAt = util.GetWeekZeroTime(now).AddDate(0, 0, 7).Add(2 * time.Hour)
|
||
|
|
case "3":
|
||
|
|
awardAt = util.GetFirstDateOfMonth(now).AddDate(0, 1, 0).Add(2 * time.Hour)
|
||
|
|
}
|
||
|
|
if awardAt.IsZero() {
|
||
|
|
log.Error("get award time err, %+v, %s", *rank, rankCycle)
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
timerKey := getRankTimerKey(rankType, rankCycle)
|
||
|
|
rankTimer, ok := rankTimerMap[timerKey]
|
||
|
|
if ok {
|
||
|
|
// notice: 覆盖之前的
|
||
|
|
rankTimer.ctxCancel()
|
||
|
|
}
|
||
|
|
ctx, cancel := context.WithCancel(context.Background())
|
||
|
|
rankTimerMap[timerKey] = &rankTicker{
|
||
|
|
timer: time.NewTicker(awardAt.Sub(time.Now())),
|
||
|
|
ctx: ctx,
|
||
|
|
ctxCancel: cancel,
|
||
|
|
rankType: rankType,
|
||
|
|
rankCycle: rankCycle,
|
||
|
|
activityId: rank.ID,
|
||
|
|
}
|
||
|
|
go func(ticker *rankTicker) {
|
||
|
|
defer util.Recover()
|
||
|
|
for {
|
||
|
|
select {
|
||
|
|
case <-ticker.timer.C:
|
||
|
|
rankAward(ticker)
|
||
|
|
case <-ticker.ctx.Done():
|
||
|
|
ticker.timer.Stop()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}(rankTimerMap[timerKey])
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// rankAward 发奖逻辑
|
||
|
|
func rankAward(ticker *rankTicker) {
|
||
|
|
rankConfig := GetConfigRankById(ticker.activityId)
|
||
|
|
if rankConfig == nil {
|
||
|
|
log.Error("rankAward, get rankConfig wrong, %d", ticker.activityId)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
_, ok := rankConfig.RankCycleMap[ticker.rankCycle]
|
||
|
|
if !ok {
|
||
|
|
log.Error("rankAward, get rankConfig by cycle wrong, %s, %+v", ticker.rankCycle, *rankConfig)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
// 重刷排行榜
|
||
|
|
rankUserRefresh()
|
||
|
|
// 获取上一期排行榜时间
|
||
|
|
rankAt, jackpotKey := getRankJackpotPreKey(rankConfig.RankType, rankConfig.RankCycle)
|
||
|
|
// 获取上一期排行榜奖池
|
||
|
|
jackpot := RankJackpotPreGet(rankConfig.RankType, rankConfig.RankCycle)
|
||
|
|
// 获取上一期排行榜用户排名
|
||
|
|
rankUsers, err := rankAwardUsersGet(rankConfig.RankType, rankConfig.RankCycle, rankAt)
|
||
|
|
if err != nil {
|
||
|
|
log.Error("rankAward, %s get rank award users err, %s, %s, rank:%+v", jackpotKey, err.Error(), rankConfig.RankCycle, *rankConfig)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
rankAwards := make(map[int]*common.RankAward)
|
||
|
|
lessJackpot := jackpot
|
||
|
|
log.Debug("rankAward, %s jackpot:%d", jackpotKey, jackpot)
|
||
|
|
// 用户发奖
|
||
|
|
for _, v := range rankConfig.RankAwardsMap[ticker.rankCycle] {
|
||
|
|
for index := v.SmallRank; index <= v.LargeRank; index++ {
|
||
|
|
rankAwards[index] = &v
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for index, rankData := range rankUsers {
|
||
|
|
if rankData.Rank != 0 {
|
||
|
|
// todo 兼容发奖错误,重新发奖
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
var (
|
||
|
|
rank = index + 1
|
||
|
|
userAwardCount int64
|
||
|
|
userAward int
|
||
|
|
)
|
||
|
|
award, exist := rankAwards[rank]
|
||
|
|
if exist {
|
||
|
|
userAward = award.AwardRate
|
||
|
|
userAwardCount = int64(userAward) * jackpot / 10000
|
||
|
|
lessJackpot -= userAwardCount
|
||
|
|
}
|
||
|
|
if userAwardCount < 0 {
|
||
|
|
userAwardCount = 0
|
||
|
|
}
|
||
|
|
err = db.Mysql().C().Model(&common.RankData{}).
|
||
|
|
Where("id = ?", rankData.ID).
|
||
|
|
Updates(map[string]interface{}{
|
||
|
|
"user_award": userAward,
|
||
|
|
"user_award_count": userAwardCount,
|
||
|
|
"rank": rank,
|
||
|
|
"updated_at": time.Now().Unix(),
|
||
|
|
}).Error
|
||
|
|
if err != nil {
|
||
|
|
log.Error("rankAward, %s update rankData err, %s", jackpotKey, err.Error())
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
// todo 给玩家邮件发奖
|
||
|
|
|
||
|
|
// 重置上一期奖池数量
|
||
|
|
if lessJackpot > 0 {
|
||
|
|
log.Debug("rankAward, %s less jackpot:%d", jackpotKey, lessJackpot)
|
||
|
|
err = db.Redis().GetRedis().Set(context.Background(), jackpotKey, lessJackpot, 0).Err()
|
||
|
|
if err != nil {
|
||
|
|
log.Error("rankAward, %s set less-jackpot err, %s", jackpotKey, err.Error())
|
||
|
|
}
|
||
|
|
_, _, _, jackpotKeyNow := getRankJackpotKey(rankConfig.RankType, rankConfig.RankCycle)
|
||
|
|
_, err = db.Redis().Incr(jackpotKeyNow, lessJackpot)
|
||
|
|
if err != nil {
|
||
|
|
log.Error("rankAward, %s:%s incr less-jackpot err, %s", jackpotKey, jackpotKeyNow, err.Error())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// UpdateRankValue 更新玩家数值(mode:1打码,2充值)
|
||
|
|
func UpdateRankValue(mode int, uid int, updateValue int64) {
|
||
|
|
configRanks := GetConfigRank(mode)
|
||
|
|
if len(configRanks) == 0 {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
now := time.Now()
|
||
|
|
for _, rank := range configRanks {
|
||
|
|
for rankCycle := range rank.RankCycleMap {
|
||
|
|
freeRate, ok := rank.FreeRatesMap[rankCycle]
|
||
|
|
if !ok {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
updateValueReal := updateValue * int64(freeRate) / 10000
|
||
|
|
rankAt, _, expired, rankKey := getRankJackpotKey(rank.RankType, rankCycle)
|
||
|
|
rankData := common.RankData{
|
||
|
|
UID: uid,
|
||
|
|
RankType: rank.RankType,
|
||
|
|
RankCycle: util.ToInt(rank.RankCycle),
|
||
|
|
RankAt: rankAt.Unix(),
|
||
|
|
RankValue: updateValueReal,
|
||
|
|
UpdatedAt: now.Unix(),
|
||
|
|
}
|
||
|
|
|
||
|
|
updates := map[string]interface{}{
|
||
|
|
"rank_value": gorm.Expr(fmt.Sprintf("rank_value + %d", rankData.RankValue)),
|
||
|
|
"updated_at": rankData.UpdatedAt,
|
||
|
|
}
|
||
|
|
_, err := db.Redis().Incr(rankKey, updateValueReal)
|
||
|
|
if err != nil {
|
||
|
|
log.Error("update rank jackpot err, %s:%s", rankKey, err.Error())
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
db.Redis().Expire(rankKey, time.Duration(expired)*time.Second)
|
||
|
|
err = db.Mysql().C().Model(&common.RankData{}).Clauses(clause.OnConflict{
|
||
|
|
Columns: []clause.Column{{Name: "rank_type"}, {Name: "rank_cycle"}, {Name: "rank_at"}, {Name: "uid"}},
|
||
|
|
DoUpdates: clause.Assignments(updates),
|
||
|
|
}).Create(&rankData).Error
|
||
|
|
if err != nil {
|
||
|
|
log.Error("update rank value err, %s, %+v", err.Error(), rankData)
|
||
|
|
_, err = db.Redis().Incr(rankKey, updateValueReal*-1)
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// RankJackpotGet 获取当期奖池信息
|
||
|
|
func RankJackpotGet(rankType int, rankCycle string) (jackpot, rankLess int64) {
|
||
|
|
_, awardAt, _, rankKey := getRankJackpotKey(rankType, rankCycle)
|
||
|
|
jackpot = db.Redis().GetInt64(rankKey)
|
||
|
|
rankLess = int64(awardAt.Sub(time.Now()).Seconds())
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// RankJackpotPreGet 获取上一期奖池
|
||
|
|
func RankJackpotPreGet(rankType int, rankCycle string) (jackpot int64) {
|
||
|
|
_, rankKey := getRankJackpotPreKey(rankType, rankCycle)
|
||
|
|
return db.Redis().GetInt64(rankKey)
|
||
|
|
}
|
||
|
|
|
||
|
|
// rankUserRefresh 刷新用户数据
|
||
|
|
func rankUserRefresh() {
|
||
|
|
// todo 根据机器人排行规则排行
|
||
|
|
}
|
||
|
|
|
||
|
|
// rankUsersGet 获取排行用户
|
||
|
|
func rankUsersGet(rankType int, rankCycle string, rankAt time.Time, page, pageSize int) (rankUsers []*common.RankDataWithUser, count int64, err error) {
|
||
|
|
log.Debug("rankCycle:%s, rankAt:%d", rankCycle, rankAt.Unix())
|
||
|
|
// 获取总数
|
||
|
|
countQuery := db.Mysql().C().
|
||
|
|
Model(&common.RankData{}).
|
||
|
|
Where("rank_type = ? and rank_cycle = ? and rank_at = ?",
|
||
|
|
rankType, util.ToInt(rankCycle), rankAt.Unix())
|
||
|
|
|
||
|
|
err = countQuery.Count(&count).Error
|
||
|
|
if err != nil {
|
||
|
|
log.Error("get rank user count err, %s", err.Error())
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
query := db.Mysql().C().
|
||
|
|
Table("rank_data rd").
|
||
|
|
Select("rd.*, u.*").
|
||
|
|
Joins("LEFT JOIN users u ON rd.uid = u.id").
|
||
|
|
Where("rd.rank_type = ? and rd.rank_cycle = ? and rd.rank_at = ?",
|
||
|
|
rankType, util.ToInt(rankCycle), rankAt.Unix()).
|
||
|
|
Order("rd.rank_value desc, rd.updated_at").
|
||
|
|
Offset((page - 1) * pageSize).
|
||
|
|
Limit(pageSize)
|
||
|
|
|
||
|
|
err = query.Find(&rankUsers).Error
|
||
|
|
if err != nil {
|
||
|
|
log.Error("get rank user with info err, %s", err.Error())
|
||
|
|
return
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// RankUsersGet 获取当前排行榜用户
|
||
|
|
func RankUsersGet(rankType int, rankCycle string, page, pageSize int) (rankUsers []*common.RankDataWithUser, count int64, err error) {
|
||
|
|
rankAt, _, _, _ := getRankJackpotKey(rankType, rankCycle)
|
||
|
|
if rankAt.Sub(time.Now()).Minutes() > 5 {
|
||
|
|
rankUserRefresh()
|
||
|
|
}
|
||
|
|
return rankUsersGet(rankType, rankCycle, rankAt, page, pageSize)
|
||
|
|
}
|
||
|
|
|
||
|
|
// RankUsersPreGet 获取上一期排行榜用户
|
||
|
|
func RankUsersPreGet(rankType int, rankCycle string, page, pageSize int) (rankUsers []*common.RankDataWithUser, count int64, err error) {
|
||
|
|
rankAt, _ := getRankJackpotPreKey(rankType, rankCycle)
|
||
|
|
return rankUsersGet(rankType, rankCycle, rankAt, page, pageSize)
|
||
|
|
}
|
||
|
|
|
||
|
|
// rankAwardUsersGet
|
||
|
|
func rankAwardUsersGet(rankType int, rankCycle string, rankAt time.Time) (rankData []*common.RankData, err error) {
|
||
|
|
log.Debug("rankCycle:%s, rankAt:%d", rankCycle, rankAt.Unix())
|
||
|
|
mdb := db.Mysql().C().
|
||
|
|
Model(&common.RankData{}).
|
||
|
|
Where("rank_type = ? and rank_cycle = ? and rank_at = ?",
|
||
|
|
rankType, util.ToInt(rankCycle), rankAt.Unix())
|
||
|
|
|
||
|
|
err = mdb.Order("rank_value desc,updated_at").Find(&rankData).Error
|
||
|
|
if err != nil {
|
||
|
|
log.Error("get rank user with info err, %s", err.Error())
|
||
|
|
return
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// UserRankAwardRecord 玩家排行榜记录
|
||
|
|
func UserRankAwardRecord(uid, page, pageSize int) (rankUsers []common.RankData, count int64, err error) {
|
||
|
|
mdb := db.Mysql().C().Model(&common.RankData{}).Where("uid = ? and `rank` != 0", uid)
|
||
|
|
err = mdb.Count(&count).Error
|
||
|
|
if err != nil {
|
||
|
|
log.Error("get rank award count err, %s", err.Error())
|
||
|
|
return
|
||
|
|
}
|
||
|
|
err = mdb.Order("rank_at desc").Offset((page - 1) * pageSize).Limit(pageSize).Find(&rankUsers).Error
|
||
|
|
if err != nil {
|
||
|
|
log.Error("get rank award err, %s", err.Error())
|
||
|
|
return
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|