package handler import ( "encoding/json" "sort" "time" "bet24.com/log" "bet24.com/servers/common" pb "bet24.com/servers/micros/badge/proto" notification "bet24.com/servers/micros/notification/proto" user "bet24.com/servers/micros/userservices/proto" ) const ( idle_seconds = 900 // 过期时长(秒) ) // 用户信息 type userInfo struct { userId int // 用户id badgeList []*pb.UserBadge // 用户徽章 timeStamp int // 时间戳 endChan chan int } func newUserInfo(userId int) *userInfo { ret := new(userInfo) ret.userId = userId ret.endChan = make(chan int) ret.updateTimeStamp() ret.loadData() return ret } // 更新时间戳 func (ub *userInfo) updateTimeStamp() { ub.timeStamp = common.GetTimeStamp() + idle_seconds return } // 加载用户数据 func (ub *userInfo) loadData() { ub.badgeList = transGetUserBadgeList(ub.userId) ub.checkBadgeIsShow() ub.loadBadgeUnlockTime() ub.doCheck() pos := []int{1, 1, 1} for _, v := range ub.badgeList { if v.Position == 0 { pos[0] = 0 } if v.Position == 1 { pos[1] = 0 } if v.Position == 2 { pos[2] = 0 } } for _, v := range ub.badgeList { if v.Position != -1 { continue } for i := 0; i < 3; i++ { if pos[i] == 1 { ub.autoWearBadge(v.BadgeId, i) pos[i] = 0 break } } } return } // 轮询检查 func (ub *userInfo) doCheck() { ticker := time.NewTicker(10 * time.Minute) go func(t *time.Ticker) { for { select { case <-ub.endChan: t.Stop() return case <-t.C: // 每十分钟执行一次 go ub.checkBadgeExpires() } } }(ticker) } // 检查徽章是否显示 func (ub *userInfo) checkBadgeIsShow() { badgeList := []*pb.UserBadge{} for _, v := range ub.badgeList { sysBadge := mgr.getSysBadgeInfo(v.BadgeId) if sysBadge == nil { log.Debug("userInfo.checkBadgeIsShow config not found. userId[%d]", ub.userId) continue } if sysBadge.Model == 0 && !sysBadge.IsShow { continue } level, lvPoints := pb.GetLevel(v.Exps, sysBadge.Levels) if level != v.Level { addPoints := ub.addUnlockTimeAndPoint(level, lvPoints, v) v.Level = level v.Points = addPoints go transSaveUserBadge(ub.userId, v) } badgeList = append(badgeList, v) } ub.badgeList = badgeList } // 徽章等级的解锁时间 func (ub *userInfo) loadBadgeUnlockTime() { for k, v := range ub.badgeList { if v.Level >= pb.UsageLevel { ub.badgeList[k].Wearable = true } var arr []string if err := json.Unmarshal([]byte(v.ObtainDate), &arr); err != nil { log.Debug("userInfo.loadBadgeUnlockTime Unmarshal fail. err:%+v", err) continue } for _, s := range arr { ub.badgeList[k].UnlockTime = append(ub.badgeList[k].UnlockTime, int(common.ParseTime(s).Unix())) } } } // 析构,停止计时器等 func (ub *userInfo) destructor() { close(ub.endChan) } // 检查徽章过期 func (ub *userInfo) checkBadgeExpires() { for k, v := range ub.badgeList { if v.ExpiresTime == 0 { continue } if !v.IsExpires() { continue } ub.badgeList[k].Level = 0 ub.badgeList[k].Position = -1 ub.badgeList[k].ExpiresTime = 0 go transSaveUserBadge(ub.userId, v) go user.UpdateUserInfo(ub.userId) log.Debug("userInfo.checkBadgeExpires userId=%d badgeId=%d 已取下所有用户对应的荣耀徽章", ub.userId, v.BadgeId) } } // 是否过期 func (ub *userInfo) isExpire() bool { return ub.timeStamp < common.GetTimeStamp() } // 获取用户徽章列表 func (ub *userInfo) getUserBadgeList() []*pb.UserBadge { return ub.badgeList } // 获取用户佩戴与展示的徽章 func (ub *userInfo) getBadgeWearAndShow() []pb.BadgePosition { var list []pb.BadgePosition for _, v := range mgr.sysList { if v.Model == 0 && !v.IsShow { continue } info := ub.getUserBadge(v.BadgeId) if info == nil { continue } if v.Model == 0 && info.Position < 0 { continue } if info.Level < pb.UsageLevel { continue } list = append(list, pb.BadgePosition{ Position: info.Position, BadgeId: info.BadgeId, Level: info.Level, }) } sort.SliceStable(list, func(i, j int) bool { return list[i].Position < list[j].Position }) return list } func (ub *userInfo) autoWearBadge(badgeId, position int) { userBadge := ub.getUserBadge(badgeId) if userBadge == nil || !userBadge.IsWearable() { return } oldPosition := -1 existBadgeId := 0 // 如果已佩戴,先卸掉 for _, v := range ub.badgeList { if v.BadgeId == badgeId { // 如果已佩戴,可能涉及交换位置 oldPosition = v.Position v.Position = -1 } // 找出占位徽章ID if position > -1 && v.Position == position { existBadgeId = v.BadgeId } } // 如果卸下 if position == -1 { go transSaveUserBadge(ub.userId, userBadge) user.UpdateUserInfo(ub.userId) return } sysBadge := mgr.getSysBadgeInfo(badgeId) if sysBadge == nil { log.Debug("userInfo.wearBadge config not found. userId[%d]", ub.userId) return } for _, v := range ub.badgeList { if v.BadgeId == badgeId { // 禁止换到新位置 if existBadgeId == 0 { if !sysBadge.IsShow { v.Position = oldPosition continue } } // 已佩戴的可互调位置 v.Position = position if oldPosition == -1 { if !sysBadge.IsShow { v.Position = -1 } } go transSaveUserBadge(ub.userId, userBadge) continue } // 如果存在交换 if existBadgeId == v.BadgeId { v.Position = oldPosition existBadge := ub.getUserBadge(v.BadgeId) if existBadge == nil { log.Debug("userInfo.wearBadge badgeId not fount, badgeId[%d]", v.BadgeId) continue } go transSaveUserBadge(ub.userId, existBadge) } } user.UpdateUserInfo(ub.userId) } // 佩戴成就徽章 // @param badgeId int 徽章id // @param position int 位置(-1:未佩戴,0-2对应位置) func (ub *userInfo) wearBadge(badgeId, position int) []pb.BadgePosition { userBadge := ub.getUserBadge(badgeId) if userBadge == nil || !userBadge.IsWearable() { return ub.getBadgeWearAndShow() } oldPosition := -1 existBadgeId := 0 // 如果已佩戴,先卸掉 for _, v := range ub.badgeList { if v.BadgeId == badgeId { // 如果已佩戴,可能涉及交换位置 oldPosition = v.Position v.Position = -1 } // 找出占位徽章ID if position > -1 && v.Position == position { existBadgeId = v.BadgeId } } // 如果卸下 if position == -1 { go transSaveUserBadge(ub.userId, userBadge) user.UpdateUserInfo(ub.userId) return ub.getBadgeWearAndShow() } sysBadge := mgr.getSysBadgeInfo(badgeId) if sysBadge == nil { log.Debug("userInfo.wearBadge config not found. userId[%d]", ub.userId) return ub.getBadgeWearAndShow() } for _, v := range ub.badgeList { if v.BadgeId == badgeId { // 禁止换到新位置 if existBadgeId == 0 { if !sysBadge.IsShow { v.Position = oldPosition continue } } // 已佩戴的可互调位置 v.Position = position if oldPosition == -1 { if !sysBadge.IsShow { v.Position = -1 } } go transSaveUserBadge(ub.userId, userBadge) continue } // 如果存在交换 if existBadgeId == v.BadgeId { v.Position = oldPosition existBadge := ub.getUserBadge(v.BadgeId) if existBadge == nil { log.Debug("userInfo.wearBadge badgeId not fount, badgeId[%d]", v.BadgeId) continue } go transSaveUserBadge(ub.userId, existBadge) } } user.UpdateUserInfo(ub.userId) return ub.getBadgeWearAndShow() } // 获取用户徽章 func (ub *userInfo) getUserBadge(badgeId int) *pb.UserBadge { for k, v := range ub.badgeList { if v.BadgeId == badgeId { return ub.badgeList[k] } } return nil } // 触发动作,根据经验值重新计算 level func (ub *userInfo) doAction(action, progress int, param pb.Scope) { // 消耗金币暂不处理,金币进度只统计购买道具消耗金币 if action == pb.Action_ConsumeGold { return } now := common.GetNowTime() //currIndex := common.GetWeekIndex(int(now.Unix())) //crdateTime := int(common.ParseTime(param.RankCrdate).Unix()) //crdateIndex := common.GetWeekIndex(crdateTime) //multiple := currIndex - crdateIndex + 1 // badge 信息 for _, v := range mgr.sysList { if v.Action != action { continue } if !v.IsCollection { log.Debug("userInfo.doAction Currently badges are not collected. userId[%d], action[%d], v[%+v]", ub.userId, action, v) continue } if pb.Action_Game_CallPoints == action && !pb.CheckRankingScore(v.BadgeScope.Ranks, param.RankInfo) { log.Debug("userInfo.doAction Insufficient score. userId[%d], action[%d], RankInfo[%+v], v[%+v]", ub.userId, action, param.RankInfo, v) continue } if len(v.BadgeScope.GameNames) > 0 && !v.IsApplicative(param) { log.Debug("userInfo.doAction gameNames config nod found. userId[%d], action[%d], GameName[%s], v[%+v]", ub.userId, action, param.GameName, v) continue } if len(v.BadgeScope.GiftIds) > 0 && !pb.GameMatch(v.BadgeScope.GiftIds, param.GiftId) { log.Debug("userInfo.doAction giftId config nod found. userId[%d], action[%d], GiftId[%+v], v[%+v]", ub.userId, action, param.GiftId, v) continue } if len(v.BadgeScope.ItemIds) > 0 && !pb.GiftIdMatch(v.BadgeScope.ItemIds, param.Items) { log.Debug("userInfo.doAction itemId config nod found. userId[%d], action[%d], ItemId[%+v], v[%+v]", ub.userId, action, param.Items, v) continue } if len(v.BadgeScope.TaskIds) > 0 && !pb.GameMatch(v.BadgeScope.TaskIds, param.TaskId) { log.Debug("userInfo.doAction taskId config nod found. userId[%d], action[%d], TaskId[%+v], v[%+v]", ub.userId, action, param.TaskId, v) continue } var isNew, isChange, isLevelChange bool var noticePoints int userBadge := ub.getUserBadge(v.BadgeId) if userBadge == nil { // 新增徽章 isNew = true userBadge = &pb.UserBadge{} userBadge.BadgeId = v.BadgeId userBadge.ObtainDate = "[]" userBadge.Position = -1 userBadge.Crdate = common.GetNowTimeStr() } // 成就徽章(生涯) if v.BadgeType == pb.BadgeType_Achievement { userBadge.Exps += progress level, lvPoints := pb.GetLevel(userBadge.Exps, v.Levels) // 计算 level、点数 if level > userBadge.Level { isChange = true isLevelChange = true addPoints := ub.addUnlockTimeAndPoint(level, lvPoints, userBadge) userBadge.Level = level userBadge.Points += addPoints noticePoints = lvPoints[level] } } // 荣耀徽章 if v.BadgeType == pb.BadgeType_Honor { if v.Action != pb.Action_NoviceGuide { // 只取零点且前三十分钟的数据才有效[00:30] if !(now.Hour() == 0 && now.Minute() < 30) { continue } if !pb.CheckRankingScore(v.BadgeScope.Ranks, param.RankInfo) { continue } } //expiresTime := crdateTime + v.Duration*multiple - 1 //if common.IsSameWeek(expiresTime, userBadge.ExpiresTime) { // // 已统计过,无需重复统计 // continue //} // TODO Start 测试,验收成功后将删除此逻辑。为了方便测试徽章到期时间,直接获取当天到零点剩余时间 endOfDay := time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 0, now.Location()) expiresTime := int(endOfDay.Unix()) if common.IsSameDay(expiresTime, userBadge.ExpiresTime) { // 已统计过,无需重复统计 continue } log.Debug("-------------------\r\n"+ "用户ID:%d\r\n"+ "榜单日期:%s\r\n"+ "榜单类型:%d\r\n"+ "榜单排名:%d\r\n"+ "当前系统榜单:%v\r\n"+ "过期时间:%s\r\n"+ "-------------------", ub.userId, param.RankCrdate, param.Type, param.Rank, v.BadgeScope.Ranks, common.TimeStampToString(endOfDay.Unix())) // TODO End // 只记录第一次解锁的时间 if len(userBadge.UnlockTime) == 0 { ub.addUnlockTimeAndPoint(0, nil, userBadge) } isChange = true userBadge.Times++ userBadge.Level = pb.MaxLevel userBadge.ExpiresTime = expiresTime } if userBadge.Level >= pb.UsageLevel { userBadge.Wearable = true } if isNew { ub.badgeList = append(ub.badgeList, userBadge) } // 存入数据库 go transSaveUserBadge(ub.userId, userBadge) if isLevelChange && userBadge.Position != -1 { ub.autoWearBadge(userBadge.BadgeId, userBadge.Position) } else { pos := []int{1, 1, 1} for _, v := range ub.badgeList { if v.Position == 0 { pos[0] = 0 } if v.Position == 1 { pos[1] = 0 } if v.Position == 2 { pos[2] = 0 } } for i := 0; i < 3; i++ { if pos[i] == 1 { ub.autoWearBadge(userBadge.BadgeId, i) break } } } // 通知客户端,属性有变化 if isChange { noticeUser := pb.UserBadge{ BadgeId: userBadge.BadgeId, Badge_Level: pb.Badge_Level{ Level: userBadge.Level, Exps: userBadge.Exps, Points: noticePoints, }, Times: userBadge.Times, Position: userBadge.Position, Wearable: userBadge.Wearable, ExpiresTime: userBadge.ExpiresTime, ObtainDate: userBadge.ObtainDate, UnlockTime: userBadge.UnlockTime, } d, _ := json.Marshal(noticeUser) notification.AddNotification(ub.userId, notification.Notification_BadgeChange, string(d)) } user.UpdateUserInfo(ub.userId) log.Debug("userInfo.doAction success!"+ "userId[%d], action[%d], progress[%d], param[%+v], userBadge[%+v], configBadge[%+v],"+ "current Time:[%s]", ub.userId, action, progress, param, userBadge, v, common.TimeStampToString(int64(common.GetTimeStamp()))) } } // 添加解锁的时间和点数 func (ub *userInfo) addUnlockTimeAndPoint(level int, lvPoints map[int]int, userBadge *pb.UserBadge) int { var point int if level != 0 && level != userBadge.Level+1 { var unlockTime []int // 处理跨等级 for i := 0; i < level; i++ { if userBadge.Level > 0 && userBadge.Level == i+1 { if len(userBadge.UnlockTime) > i { unlockTime = append(unlockTime, userBadge.UnlockTime[i]) } continue } point += lvPoints[i+1] unlockTime = append(unlockTime, common.GetTimeStamp()) } userBadge.UnlockTime = unlockTime } else { if v, ok := lvPoints[level]; ok { point = v } userBadge.UnlockTime = append(userBadge.UnlockTime, common.GetTimeStamp()) } var arr []string for _, v := range userBadge.UnlockTime { arr = append(arr, common.TimeStampToString(int64(v))) } d, _ := json.Marshal(arr) userBadge.ObtainDate = string(d) return point }