package handler import ( "math" "strconv" "strings" "time" waterpool "bet24.com/servers/micros/waterpool/proto" "bet24.com/log" "bet24.com/servers/common" pb "bet24.com/servers/micros/userlabel/proto" ) const ( seconds = 600 // 过期时长(秒) max_days = 30 // 保留X天数据 ) const ( _ = iota // 0=无效 tag_login_activeTime // 1=活跃时长(大厅模块) tag_charge_Amount // 2=付费金额(充值模块) tag_game_love_online // 3=游戏喜好时长(游戏模块:1=游戏时长、2=游戏流水、3=报名次数) tag_game_friendRoom // 4=好友房(游戏模块) tag_audioRoom_interactive // 5=互动类(语聊房模块) tag_task_award // 6=任务类(任务模块) tag_chat // 7=聊天类(聊天模块) tag_friend // 8=交际类(好友模块) tag_register_ipDanger // 9=IP危险(注册模块) tag_register_iMeiDanger // 10=IMei危险(注册模块) tag_charge_game_loseDanger // 11=输金危险(游戏模块) tag_game_cheatingDanger // 12=作弊危险(游戏模块) tag_charge_potentialCharge // 13=潜在付费(充值模块) tag_charge_passiveCharge // 14=被动付费(充值模块) tag_charge_savingsCharge // 15=储蓄付费(充值模块) tag_charge_discountCharge // 16=折扣付费(充值模块,主要用于礼包购买) tag_login_newbieBehavior // 17=新手行为(大厅模块) tag_charge_game_winDanger // 18=赚金危险(游戏模块) tag_register_utmsource // 19=注册来源渠道 tag_login_return // 20=登录回归 tag_bankruptcy // 21=破产 tag_game_love_bet // 22=游戏喜好投注 tag_game_love_match // 23=游戏喜好比赛报名 tag_charge_frequency // 24=付费频率 tag_time_love // 25=时间偏好 ) // 用户信息 type userInfo struct { userId int // 用户id label_list []*pb.LabelInfo // 标签列表 timeStamp int // 时间戳 } func newUserInfo(userId int) *userInfo { ret := new(userInfo) ret.userId = userId ret.updateTimeStamp() ret.loadData() return ret } // 加载数据 func (this *userInfo) loadData() { // TODO:数据库获取 list := trans_GetInfo(this.userId) for i := 0; i < len(list); i++ { this.label_list = append(this.label_list, &list[i]) } return } // 更新时间戳 func (this *userInfo) updateTimeStamp() { this.timeStamp = common.GetTimeStamp() + seconds return } // 是否过期 func (this *userInfo) isExpire() bool { if this.timeStamp < common.GetTimeStamp() { return true } return false } // 获取标签列表 func (this *userInfo) getLabelList() []*pb.LabelInfo { var ret []*pb.LabelInfo for _, v := range this.label_list { if v.LabelId == "" { continue } ret = append(ret, v) } return ret } // 根据类型获取标签信息 func (this *userInfo) getLabelInfo(typeId int) *pb.LabelInfo { for _, v := range this.label_list { if v.TypeId == typeId { return v } } return nil } // 设置标签 func (this *userInfo) setLabel(typeId, days int, labelId string) { // 获取标签信息 labelInfo := this.getLabelInfo(typeId) // 标签不存在 if labelInfo == nil { labelInfo = &pb.LabelInfo{ TypeId: typeId, } this.label_list = append(this.label_list, labelInfo) } // 更新标签 labelInfo.LabelId = labelId // TODO:写入数据库 go trans_Save(this.userId, days, labelInfo) } // 分配事件 func (this *userInfo) dispatch(typeId int, scope pb.Scope) { switch typeId { case pb.Type_Login: // 登录 dayIndex := common.GetDayIndex(common.GetTimeStamp()) // 注册登录 if scope.IsRegister { // 17=新手行为(大厅模块) this.handleEvent(tag_login_newbieBehavior, "", dayIndex, dayIndex) // 1=活跃时长(大厅模块) // this.handleEvent(tag_login_activeTime, "", 0, 0) // 2=付费金额(充值模块) // this.handleEvent(tag_charge_Amount, "", 0, 0) // 9=ip危险(注册模块) this.calIPAndIMei(tag_register_ipDanger, scope.IPAddress, "") // 10=IMei危险(注册模块) this.calIPAndIMei(tag_register_iMeiDanger, "", scope.IMei) // 11=注册来源渠道 this.handleEvent(tag_register_utmsource, scope.UTMSource, dayIndex, dayIndex) } else { // 正常登录 // 在线 if scope.OnlineSeconds > 0 { // 1=活跃时长(大厅模块) this.handleEvent(tag_login_activeTime, "", scope.OnlineSeconds, 0) timeOfDay := "" timeFlag := time.Now().Hour()*100 + time.Now().Minute() if timeFlag >= 500 && timeFlag < 900 { timeOfDay = "早晨" } else if timeFlag >= 900 && timeFlag < 1200 { timeOfDay = "上午" } else if timeFlag >= 1200 && timeFlag < 1400 { timeOfDay = "中午" } else if timeFlag >= 1400 && timeFlag < 1800 { timeOfDay = "下午" } else if timeFlag >= 1800 && timeFlag < 1930 { timeOfDay = "黄昏" } else if timeFlag >= 1930 && timeFlag < 2200 { timeOfDay = "晚上" } else if timeFlag < 500 || timeFlag >= 2200 { timeOfDay = "夜间" } // 25=时间偏好(大厅模块) this.handleEvent(tag_time_love, timeOfDay, scope.OnlineSeconds, 0) } // 17=新手行为(大厅模块) this.handleEvent(tag_login_newbieBehavior, "", dayIndex, 0) // 20=登录回归 this.handleEvent(tag_login_return, "", 1, 0) } case pb.Type_Charge: // 充值 payNum := 0 // 有实际充值才算 if scope.PayPrice > 0 { payNum = scope.Num } // 2=付费金额(充值模块) this.handleEvent(tag_charge_Amount, "", int(scope.PayPrice), 0) // 11=输金危险(游戏模块、充值模块) this.handleEvent(tag_charge_game_loseDanger, "", 0, int(scope.PayPrice)) // 18=赚金危险 this.handleEvent(tag_charge_game_winDanger, "", 0, int(scope.PayPrice)) // 13=潜在付费(充值模块) this.handleEvent(tag_charge_potentialCharge, "", scope.Num, int(scope.PayPrice)) // 14=被动付费(充值模块) this.handleEvent(tag_charge_passiveCharge, "", scope.GoldAmount, payNum) // 15=储蓄付费(充值模块) this.handleEvent(tag_charge_savingsCharge, "", scope.GoldAmount, int(scope.PayPrice)) // 折扣 if scope.IsDiscount { // 16=折扣付费(充值模块,主要用于礼包购买) this.handleEvent(tag_charge_discountCharge, "", payNum, payNum) } else { // 16=折扣付费(充值模块,主要用于礼包购买) this.handleEvent(tag_charge_discountCharge, "", 0, payNum) } // 24=付费频率 this.handleEvent(tag_charge_frequency, "", payNum, 0) case pb.Type_Game: // 游戏 // 好友房 if scope.IsFriendRoom { // 4=好友房(游戏模块) this.handleEvent(tag_game_friendRoom, strconv.Itoa(scope.GameId), scope.Num, 0) // 赢金币 if scope.GoldAmount > 0 { // 12=作弊危险(游戏模块、充值模块) this.handleEvent(tag_game_cheatingDanger, "", scope.GoldAmount, scope.GoldAmount) } } else { // 普通游戏 // 输金币 if scope.GoldAmount < 0 { // 11=输金危险(游戏模块、充值模块) this.handleEvent(tag_charge_game_loseDanger, "", -scope.GoldAmount, 0) } else if scope.GoldAmount > 0 { // 赢金币 // 12=作弊危险(游戏模块、充值模块) this.handleEvent(tag_game_cheatingDanger, "", 0, scope.GoldAmount) // 18=赚金危险 this.handleEvent(tag_charge_game_winDanger, "", scope.GoldAmount, 0) } } // 游戏喜好(游戏模块:3=游戏时长、22=游戏流水、23=报名次数) if scope.OnlineSeconds > 0 { this.handleEvent(tag_game_love_online, strconv.Itoa(scope.GameId), scope.OnlineSeconds, 0) } else if scope.GoldAmount != 0 { gold := int(math.Abs(float64(scope.GoldAmount))) this.handleEvent(tag_game_love_bet, strconv.Itoa(scope.GameId), gold, 0) } case pb.Type_Task: // 任务 // 6=任务类(任务模块) this.handleEvent(tag_task_award, "", scope.Num, 0) case pb.Type_Chat: // 聊天 // 7=聊天类(聊天模块) this.handleEvent(tag_chat, "", scope.Num, 0) case pb.Type_AudioRoom: // 6=语聊房 // 5=互动类(语聊房模块) this.handleEvent(tag_audioRoom_interactive, "", scope.Num, 0) case pb.Type_Friend: // 7=好友 // 8=交际类(好友模块) this.handleEvent(tag_friend, "", scope.Num, 0) case pb.Type_Bankruptcy: // 8=破产 // 21=破产 this.handleEvent(tag_bankruptcy, "", scope.Num, 0) default: log.Error("user.dispatch userId=%d typeId=%d scope=%+v is not dealt ", this.userId, typeId, scope) } return } // 处理事件 func (this *userInfo) handleEvent(typeId int, param string, value int, totalValue int) { dayIndex := common.GetDayIndex(common.GetTimeStamp()) var ( labelInfo *pb.LabelInfo dayInfo *pb.DayInfo ) // 标签列表 for _, v := range this.label_list { if v.TypeId != typeId { continue } labelInfo = v break } // 标签不存在 if labelInfo == nil { labelInfo = &pb.LabelInfo{ TypeId: typeId, } this.label_list = append(this.label_list, labelInfo) } // 累计类(如:充值、输金风险、被动付费、储蓄付费、折扣付费、交际类、登录回归) if typeId == tag_charge_game_loseDanger || typeId == tag_charge_game_winDanger || typeId == tag_charge_passiveCharge || typeId == tag_charge_savingsCharge || typeId == tag_charge_discountCharge || typeId == tag_friend || typeId == tag_login_return { labelInfo.TotalValue += totalValue } else if typeId == tag_login_newbieBehavior { // 新手行为 if totalValue > 0 { labelInfo.TotalValue = totalValue } } // 每天数据 for _, v := range labelInfo.Days { // 判断日期 if v.Index != dayIndex { continue } // 判断游戏ID if v.Param != param { continue } dayInfo = v } // 每天数据不存在 if dayInfo == nil { dayInfo = &pb.DayInfo{ Index: dayIndex, Param: param, Value: 0, } labelInfo.Days = append(labelInfo.Days, dayInfo) } // 保留 max_days 天数据 if count := len(labelInfo.Days); count > max_days { labelInfo.Days = labelInfo.Days[count-max_days:] } // 作弊危险 if typeId == tag_game_cheatingDanger { // 获取配置信息 cfg := mgr.getConfigInfo(labelInfo.TypeId) if cfg == nil { log.Error("user.triggerCounter userId=%d typeId=%d", this.userId, typeId) return } for _, v := range cfg.Content { if value < v.Min && totalValue < v.Min { return } } labelInfo.TotalValue += totalValue } else if typeId == tag_charge_potentialCharge { // 潜在付费 dayInfo.ExtValue = totalValue } // 储蓄付费、交际类、新手行为、登录回归,不累计 if typeId == tag_charge_savingsCharge || typeId == tag_friend || typeId == tag_login_newbieBehavior || typeId == tag_login_return { dayInfo.Value = value } else if typeId == tag_charge_passiveCharge { // 被动付费 // 获取配置信息 cfg := mgr.getConfigInfo(labelInfo.TypeId) if cfg == nil { log.Error("user.triggerCounter userId=%d typeId=%d", this.userId, typeId) return } for _, v := range cfg.Content { if value <= v.Min { dayInfo.Value += totalValue break } } } else { // 累计 dayInfo.Value += value } go func() { var ret pb.CalResult // 计算用户标签 if ret = this.cal(labelInfo, value); ret.Success && labelInfo.LabelId != ret.LabelId { // log.Debug("handleEvent userId=%d ret=%+v", this.userId, ret) labelInfo.LabelId = ret.LabelId if ret.PoolValue != 0 { // 触发水池 go waterpool.GrantUserNewWaterPool(this.userId, ret.PoolValue, ret.LabelName) } } // TODO:存入数据库 trans_Save(this.userId, ret.Days, labelInfo) }() } // 根据用户数据计算标签 func (this *userInfo) cal(labelInfo *pb.LabelInfo, value int) pb.CalResult { var ret pb.CalResult // 获取配置信息 cfg := mgr.getConfigInfo(labelInfo.TypeId) if cfg == nil { log.Error("user.cal userId=%d typeId=%d", this.userId, labelInfo.TypeId) return ret } total, totalExt, dayIndex := 0, 0, common.GetDayIndex(common.GetTimeStamp()) switch labelInfo.TypeId { case tag_login_activeTime: // 1=活跃时长 for _, v := range labelInfo.Days { if cfg.Days > 0 && v.Index < dayIndex-cfg.Days { continue } total += v.Value } avg := total / cfg.Days for _, v := range cfg.Content { if v.Min <= avg && avg <= v.Max { ret = pb.CalResult{ Success: true, LabelId: v.LabelId, LabelName: v.LabelName, PoolValue: v.PoolValue, Days: cfg.Days, } return ret } } case tag_charge_Amount: // 2=付费金额 fallthrough case tag_game_friendRoom: // 4=好友房 fallthrough case tag_audioRoom_interactive: // 5=互动类 fallthrough case tag_task_award: // 6=任务类 fallthrough case tag_chat: // 7=聊天类 fallthrough case tag_bankruptcy: // 21=破产 fallthrough case tag_game_love_online: // 3=游戏喜好时长 fallthrough case tag_game_love_bet: // 22=游戏喜好流水 fallthrough case tag_game_love_match: // 23=游戏喜好比赛报名 fallthrough case tag_charge_frequency: // 24=付费频率 for _, v := range labelInfo.Days { if cfg.Days > 0 && v.Index < dayIndex-cfg.Days { continue } total += v.Value } for _, v := range cfg.Content { if v.Min <= total && total <= v.Max { ret = pb.CalResult{ Success: true, LabelId: v.LabelId, LabelName: v.LabelName, PoolValue: v.PoolValue, Days: cfg.Days, } return ret } } case tag_friend: // 8=交际类 fallthrough case tag_login_return: // 20=登录回归 for _, v := range cfg.Content { if labelInfo.TotalValue >= v.Min { ret = pb.CalResult{ Success: true, LabelId: v.LabelId, LabelName: v.LabelName, PoolValue: v.PoolValue, Days: cfg.Days, } return ret } } case tag_charge_potentialCharge: // 13=潜在付费 for _, v := range labelInfo.Days { if cfg.Days > 0 && v.Index < dayIndex-cfg.Days { continue } total += v.Value totalExt += v.ExtValue } for _, v := range cfg.Content { if v.Min <= total && v.Ext >= totalExt { ret = pb.CalResult{ Success: true, LabelId: v.LabelId, LabelName: v.LabelName, PoolValue: v.PoolValue, Days: cfg.Days, } return ret } } case tag_register_ipDanger: // 9=IP危险 // Nothing(manager已处理) case tag_register_iMeiDanger: // 10=IMei危险 // Nothing(manager已处理) case tag_charge_game_loseDanger: // 11=输金危险 fallthrough case tag_charge_game_winDanger: // 18=赚金危险 for _, v := range labelInfo.Days { if cfg.Days > 0 && v.Index < dayIndex-cfg.Days { continue } total += v.Value } for _, v := range cfg.Content { if total > 0 && total >= int(float64(labelInfo.TotalValue*v.Ext)*(float64(v.Percentage)/100.00)) && total >= v.Min { ret = pb.CalResult{ Success: true, LabelId: v.LabelId, LabelName: v.LabelName, PoolValue: v.PoolValue, Days: cfg.Days, } return ret } } case tag_game_cheatingDanger: // 12=作弊危险 fallthrough case tag_charge_passiveCharge: // 14=被动付费 fallthrough case tag_charge_discountCharge: // 16=折扣付费 for _, v := range labelInfo.Days { if cfg.Days > 0 && v.Index < dayIndex-cfg.Days { continue } total += v.Value } for _, v := range cfg.Content { if total > 0 && total >= int(float64(labelInfo.TotalValue)*(float64(v.Percentage)/100.00)) { ret = pb.CalResult{ Success: true, LabelId: v.LabelId, LabelName: v.LabelName, PoolValue: v.PoolValue, Days: cfg.Days, } return ret } } case tag_charge_savingsCharge: // 15=储蓄付费 for _, v := range cfg.Content { if value >= int(float64(labelInfo.TotalValue*v.Ext)*(float64(v.Percentage)/100.00)) && value >= v.Min { ret = pb.CalResult{ Success: true, LabelId: v.LabelId, LabelName: v.LabelName, PoolValue: v.PoolValue, Days: cfg.Days, } return ret } } case tag_login_newbieBehavior: // 17=新手行为 // total = labelInfo.Days[len(labelInfo.Days)-1].Index - labelInfo.TotalValue + 1 if cfg.Days > 0 && labelInfo.TotalValue < labelInfo.Days[len(labelInfo.Days)-1].Index-cfg.Days { ret = pb.CalResult{ Success: true, LabelId: "", LabelName: "", PoolValue: 0, Days: cfg.Days, } return ret } total = len(labelInfo.Days) for _, v := range cfg.Content { if v.Min <= total && total <= v.Max { // log.Debug("userId=%d v=%+v", this.userId, v) ret = pb.CalResult{ Success: true, LabelId: v.LabelId, LabelName: v.LabelName, PoolValue: v.PoolValue, Days: cfg.Days, } return ret } } case tag_register_utmsource: // 19=新注册来源 utmSource := "" for _, v := range labelInfo.Days { if cfg.Days > 0 && v.Index < dayIndex-cfg.Days { continue } utmSource = strings.ToLower(v.Param) } for _, v := range cfg.Content { if !strings.Contains(utmSource, strings.ToLower(v.LabelName)) { continue } ret = pb.CalResult{ Success: true, LabelId: v.LabelId, LabelName: v.LabelName, PoolValue: v.PoolValue, Days: cfg.Days, } return ret } case tag_time_love: // 25=时间偏好 sum_list := make(map[string]int) for _, v := range labelInfo.Days { if cfg.Days > 0 && v.Index < dayIndex-cfg.Days { continue } vu, ok := sum_list[v.Param] if ok { vu += v.Value } sum_list[v.Param] = vu } if len(sum_list) <= 0 { break } var ( max_value int max_labelId string max_labelName string max_poolValue int ) for _, v := range cfg.Content { vu, ok := sum_list[v.LabelName] if !ok { continue } if vu < max_value { continue } max_value = vu max_labelId = v.LabelId max_labelName = v.LabelName max_poolValue = v.PoolValue } ret = pb.CalResult{ Success: true, LabelId: max_labelId, LabelName: max_labelName, PoolValue: max_poolValue, Days: cfg.Days, } return ret } ret = pb.CalResult{ Success: true, LabelId: "", LabelName: "", PoolValue: 0, Days: cfg.Days, } return ret } // IPAddress、IMei 类计算 func (this *userInfo) calIPAndIMei(typeId int, ipAddress, iMei string) { // 获取配置信息 cfg := mgr.getConfigInfo(typeId) if cfg == nil { log.Error("user.calIPAndIMei userId=%d typeId=%d ipAddress=%s iMei=%s", this.userId, typeId, ipAddress, iMei) return } for _, v := range cfg.Content { // TODO:从数据库查询 users := trans_GetIPAndIMei(ipAddress, iMei, cfg.Days) // 没有达到用户数 if len(users) < v.Min { continue } // 符合标签的用户 for _, uId := range users { // 获取当前用户 u := mgr.getUser(uId) if u == nil { log.Debug("manager.calIPAndIMei userId=%d typeId=%d ipAddress=%s iMei=%s ", uId, typeId, ipAddress, iMei) continue } // 设置标签 u.setLabel(typeId, cfg.Days, v.LabelId) } } return }