印度包网
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.

507 lines
16 KiB

3 months ago
package call
import (
"context"
2 months ago
"encoding/json"
3 months ago
"fmt"
"github.com/liangdas/mqant/log"
"gorm.io/gorm"
"gorm.io/gorm/clause"
3 months ago
"math/rand"
3 months ago
"server/common"
"server/db"
"server/util"
3 months ago
"sort"
3 months ago
"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) {
2 months ago
now := time.Now()
3 months ago
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
}
// 重刷排行榜
3 months ago
rankUserRefresh(ticker.rankType, true)
3 months ago
// 获取上一期排行榜时间
2 months ago
rankAt, jackpotKey := getRankJackpotPreKey(rankConfig.RankType, ticker.rankCycle)
3 months ago
// 获取上一期排行榜奖池
2 months ago
jackpot := RankJackpotPreGet(rankConfig.RankType, ticker.rankCycle)
3 months ago
// 获取上一期排行榜用户排名
2 months ago
rankUsers, err := rankAwardUsersGet(rankConfig.RankType, ticker.rankCycle, rankAt)
3 months ago
if err != nil {
2 months ago
log.Error("rankAward, %s get rank award users err, %s, %s, rank:%+v", jackpotKey, err.Error(), ticker.rankCycle, *rankConfig)
3 months ago
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
}
2 months ago
if userAwardCount == 0 {
continue
}
2 months ago
awardData, _ := json.Marshal(common.CurrencyPair{
Type: common.CurrencyINR,
Value: userAwardCount,
})
var rankCycle string
switch ticker.rankCycle {
case "1":
rankCycle = "Day"
case "2":
rankCycle = "Week"
case "3":
rankCycle = "Month"
}
3 months ago
// todo 给玩家邮件发奖
2 months ago
mail := common.Mail{
DraftID: "",
Sender: "system",
Receiver: rankData.UID,
Type: 1,
Title: fmt.Sprintf("%s Rank Rankings settlement(%s)", rankCycle, rankAt.Format("20060102")),
Content: fmt.Sprintf("Nice work! You ranked %dth on the betting leaderboard and earned a reward. Go claim it now!", rank),
Enc: string(awardData),
SendMethod: 1,
Status: common.MailStatusNew,
Time: now.Unix(),
}
err = db.Mysql().C().Model(&common.Mail{}).Create(&mail).Error
if err != nil {
log.Error("create mail err, %s, %+v", err.Error(), mail)
}
2 months ago
}
// 重置上一期奖池数量
2 months ago
preJackpotLessKey := jackpotKey + "|less"
2 months ago
log.Debug("rankAward, %s, %d, less jackpot:%d", jackpotKey, jackpot, lessJackpot)
2 months ago
err = db.Redis().GetRedis().Set(context.Background(), preJackpotLessKey, lessJackpot, time.Hour*24*30*2).Err()
2 months ago
if err != nil {
2 months ago
log.Error("share rankAward, %s set less-jackpot err, %s", preJackpotLessKey, err.Error())
2 months ago
}
if lessJackpot > 0 {
2 months ago
_, _, _, jackpotKeyNow := getRankJackpotKey(rankConfig.RankType, ticker.rankCycle)
2 months ago
_, err = db.Redis().Incr(jackpotKeyNow, lessJackpot)
if err != nil {
log.Error("rankAward, %s:%s incr less-jackpot err, %s", jackpotKey, jackpotKeyNow, err.Error())
3 months ago
}
}
}
// UpdateRankValue 更新玩家数值(mode:1打码,2充值)
3 months ago
// todo 接入打码
func UpdateRankValue(mode int, uid int, updateValue int64, isRobot int) {
3 months ago
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(rankCycle),
3 months ago
RankAt: rankAt.Unix(),
IsRobot: isRobot,
3 months ago
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)
}
2 months ago
func RankJackpotPreLessGet(rankType int, rankCycle string) (jackpot int64) {
_, rankKey := getRankJackpotPreKey(rankType, rankCycle)
return db.Redis().GetInt64(rankKey + "less")
}
3 months ago
// rankUserRefresh 刷新用户数据
3 months ago
func rankUserRefresh(rankType int, must bool) {
// todo must为false可以使用缓存
var err error
rankList := GetConfigRank(rankType)
for _, rankCycle := range []string{"3", "2", "1"} {
for _, rankCfg := range rankList {
if _, ok := rankCfg.RankCycleMap[rankCycle]; !ok {
continue
}
if rankCfg.RobotCfgValue == nil {
continue
}
if rankCfg.RobotCfgValue.ChangeType == 2 {
continue
}
robotCfg := rankCfg.RobotCfgValue
rankAt, _, _, _ := getRankJackpotKey(rankType, rankCycle)
var rankRobots []common.RankData
err = db.Mysql().C().Model(&common.RankData{}).Where("rank_type = ? and rank_cycle = ? and rank_at = ? and is_robot = 1", rankType, util.ToInt(rankCycle), rankAt.Unix()).
Order("rank_value desc").Find(&rankRobots).Error
3 months ago
if err != nil {
log.Error("get robot data err, %s", err.Error())
continue
}
if needRobotCount := len(robotCfg.RobotRankMap) - len(rankRobots); needRobotCount > 0 {
var robots []common.PlayerDBInfo
err = db.Mysql().C().Model(&common.PlayerDBInfo{}).Where("openid like 'robot%'").Order("RAND()").Limit(needRobotCount).Find(&robots).Error
if err != nil {
log.Error("get robot info err, %s", err.Error())
continue
}
if len(robots) == 0 {
log.Error("get robot wrong")
continue
}
for _, robot := range robots {
rankRobots = append(rankRobots, common.RankData{
UID: robot.Id,
RankType: rankType,
RankCycle: util.ToInt(rankCycle),
RankAt: rankAt.Unix(),
RankValue: 0,
IsRobot: 1,
UpdatedAt: time.Now().Unix(),
})
}
}
var rankUsers []*common.RankData
maxRank := robotCfg.RobotRankArray[len(robotCfg.RobotRankArray)-1]
err = db.Mysql().C().Model(&common.RankData{}).Where("rank_type = ? and rank_cycle = ? and rank_at = ?",
rankType, util.ToInt(rankCycle), rankAt.Unix()).Order("rank_value desc").
Limit(maxRank).Find(&rankUsers).Error
if err != nil {
log.Error("get rank user err, %s", err.Error())
continue
}
freeRate := rankCfg.FreeRatesMap[rankCycle]
if freeRate == 0 {
log.Error("freeRate is nil, %d:%s", rankType, rankCycle)
continue
}
rankRobotsMap := make(map[int]struct{})
rankUserMap := make(map[int]*common.RankData)
for _, v := range rankRobots {
rankRobotsMap[v.UID] = struct{}{}
}
for _, v := range rankUsers {
rankUserMap[v.UID] = v
}
if len(rankUsers) == 0 { // 没有玩家上榜,随机一个值上去
for _, rankRobot := range rankRobots {
addValue := rand.Intn(100) + 1
updateValue := addValue * 10000 / freeRate
if rankRobot.RankValue == 0 {
UpdateRankValue(rankType, rankRobot.UID, int64(updateValue), 1)
3 months ago
}
}
} else {
for _, rank := range robotCfg.RobotRankArray {
rankUser := rankUsers[rank-1]
if rankUser.IsRobot == 1 {
delete(rankRobotsMap, rankUser.UID)
continue
}
rankValue := rankUser.RankValue // 当前排位的值
for _, robot := range rankRobots { // 从大到小找一个没重置过值的机器人
if _, ok := rankRobotsMap[robot.UID]; !ok {
continue
}
addFloat := rand.Intn(robotCfg.FloatMax-robotCfg.FloatMin) + robotCfg.FloatMin
rankValueFinal := rankValue + rankValue*int64(addFloat)/100
addValue := rankValueFinal - robot.RankValue
updateValue := addValue * 10000 / int64(freeRate)
if updateValue > 0 {
UpdateRankValue(rankType, robot.UID, updateValue, 1)
3 months ago
delete(rankRobotsMap, rankUser.UID)
}
_, ok := rankUserMap[robot.UID]
if !ok { // 不存在新增
robot.RankValue = rankValueFinal
rankUserMap[robot.UID] = &robot
rankUsers = append(rankUsers, &robot)
} else { // 存在则更新数值
rankUserMap[robot.UID].RankValue = rankValueFinal
}
sort.Slice(rankUsers, func(i, j int) bool {
return rankUsers[i].RankValue > rankUsers[j].RankValue
})
}
}
}
}
}
3 months ago
}
// 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 time.Now().Sub(rankAt).Minutes() > 5 {
3 months ago
rankUserRefresh(rankType, false)
3 months ago
}
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
}