package model import ( "active/constant" "active/tools" "database/sql" "encoding/json" "fmt" "strconv" "strings" "sync" "time" "git.jiaxianghudong.com/go/gsdkclient" "git.jiaxianghudong.com/go/logs" "git.jiaxianghudong.com/go/xlog" "git.jiaxianghudong.com/webs/pkg/dbx" "git.jiaxianghudong.com/webs/pkg/rds" ) //GoodsCache 调用到的计费点缓存 var GoodsCache sync.Map var PayBlackCache sync.Map //PayBlackCacheNew 锁机制 var PayBlackCacheNew *PbCacheMap var VerIPBlackCacheNew *VerIpCacheMap const MaxIpNum = 50 type PbCacheMap struct { Data map[int]int Lock *sync.RWMutex } func NewPayBlackCacheNew() *PbCacheMap { return &PbCacheMap{ Data: make(map[int]int), Lock: &sync.RWMutex{}, } } func (d *PbCacheMap) Get(k int) (int, bool) { d.Lock.RLock() defer d.Lock.RUnlock() v, ok := d.Data[k] return v, ok } func (d *PbCacheMap) Set(k int, v int) { d.Lock.Lock() defer d.Lock.Unlock() d.Data[k] = v } type VerIpCacheMap struct { Data map[string][]string Lock *sync.RWMutex } func NewVerIpCacheNew() *VerIpCacheMap { return &VerIpCacheMap{ Data: make(map[string][]string), Lock: &sync.RWMutex{}, } } func (d *VerIpCacheMap) Get(k string) ([]string, bool) { d.Lock.RLock() defer d.Lock.RUnlock() v, ok := d.Data[k] return v, ok } func (d *VerIpCacheMap) Set(k string, v []string) { d.Lock.Lock() defer d.Lock.Unlock() d.Data[k] = v } //BlackIPDuan 审核人员IP段 var BlackIPDuan = []string{"182.254.115", "59.37.125"} //BlackIPs 审核人员IP var BlackIPs = []string{"111.222.19.134"} var TestUserIDS = []int{101832265, 125660976, 101758654, 101616296, 56409319} func GetBlackByVerIP(ver, ip string, appid int) bool { vkey := fmt.Sprintf("%d-%s", appid, ver) //判断大于 50个不同IP就返回 黑名单关闭 if barr, ok := VerIPBlackCacheNew.Get(vkey); !ok { VerIPBlackCacheNew.Set(vkey, []string{ip}) return true } else { if len(barr) >= MaxIpNum { return false } else { //未达到50次 if !tools.InStringArray(ip, barr) { barr = append(barr, ip) VerIPBlackCacheNew.Set(vkey, barr) } return true } } } //GetGoods 获取计费点缓存 func GetGoods(vcfrom string) map[string]interface{} { //先读取缓存 var goodsret string goodsKey := fmt.Sprintf("%s%s", vcfrom, constant.GOODSKEY) if data, ok := GoodsCache.Load(goodsKey); ok { logs.Debugf("cache key: %s", goodsKey) return data.(map[string]interface{}) } allgoods := make(map[string]interface{}) goodsret = rds.Redis8.Get(goodsKey).Val() err := json.Unmarshal([]byte(goodsret), &allgoods) if err != nil { logs.Error(fmt.Sprintf("goodsret: %s err: %v", goodsret, err)) return allgoods } var goodsconfigs = make(map[string]interface{}) if data, ok := allgoods[constant.GOODSVERSION]; ok { if val, ok := data.(map[string]interface{}); ok { goodsconfigs = val } } fmt.Printf("【goods configs】: key:%s %+v", goodsKey, goodsconfigs) GoodsCache.Store(goodsKey, goodsconfigs) return goodsconfigs } //InBlacklist 获取黑名单 func InBlacklist(userid string) bool { if data, ok := PayBlackCache.Load(constant.PAYBLACKLIST); ok { logs.Debugf("PayBlackCache cache key: %s", constant.PAYBLACKLIST) return tools.InStringArray(userid, data.([]string)) } blist := rds.Redis8.SMembers(constant.PAYBLACKLIST).Val() fmt.Printf(" \n【pay black configs】: %+v", blist) PayBlackCache.Store(constant.PAYBLACKLIST, blist) return tools.InStringArray(userid, blist) } //GetBlackList 获取黑名单 func GetBlackList(userid string, ip, UA string) bool { var ipDuan string ipArr := strings.Split(ip, ".") if len(ipArr) >= 3 { ipDuan = strings.Join(ipArr[0:3], ".") } // 1. 根据ip段来判断如果是黑名单IP段, 用户直接拉入黑名单 if GetBlackIPDuanCache(ipDuan) { var count int uID, _ := strconv.Atoi(userid) userInfo := gsdkclient.Client().UserInfoNoLoginData(uID) temp := gsdkclient.Client().GetEffortEx(uint(uID), []int{2}, false) //获取局数 if temp != nil { count = int(temp[2]) //局数 } xlog.Infof("【 黑名单网段玩家 】 %v ip:%s agent:%v", userid, ip, UA) if userIp := rds.Redis8.HGet(constant.PAYBLACKUSERHASH, userid).Val(); userIp != "" { rds.Redis8.HSet(constant.PAYBLACKUSERHASHTMP, userid, fmt.Sprintf("%s|局数:%d|注册时间:%s", ip, count, userInfo.RegDate)) } return true } if userIp := rds.Redis8.HGet(constant.PAYBLACKUSERHASH, userid).Val(); userIp != "" { xlog.Infof("【 审核玩家 】 %v ip:%s agent:%v", userid, ip, UA) rds.Redis8.HIncrBy(constant.PAYBLACKIPHASH, ip, 1) //黑名单玩家计入使用该ip次数 return true } // hour := time.Now().Hour() //高峰期不执行查询操作, 小于晚上8点,或者大于等于10点 进行特定IP认证 // if hour < 20 || hour >= 22 { // //2. 不是IP段再确认玩家是否黑名单玩家 // if userIp := rds.Redis8.HGet(constant.PAYBLACKUSERHASH, userid).Val(); userIp != "" { // rds.Redis8.HIncrBy(constant.PAYBLACKIPHASH, ip, 1) //黑名单玩家计入使用该ip次数 // return true // } // // //3. 不是黑名单玩家再判断是否 具体黑名单IP并且达到拉黑值 // // if blackip := rds.Redis8.HGet(constant.PAYBLACKIPHASH, ip).Val(); blackip != "" { //观察黑名单IP列表名单 // // if c, _ := strconv.Atoi(blackip); c >= 5 { //超过黑名单IP次数,加入黑名单用户 // // rds.Redis8.HSet(constant.PAYBLACKUSERHASH, userid, ip) // // return true // // } // // } // } return false } //GetBlackIPDuanCache 获取黑名单ip段缓存 func GetBlackIPDuanCache(ipDuan string) bool { // if data, ok := PayBlackIPDuanCache.Load(constant.PAYBLACKIPRANGE); ok { // logs.Debugf("PayBlackIPDuanCache cache key: %s", constant.PAYBLACKIPRANGE) // return tools.InStringArray(ipDuan, data.([]string)) // } //blist := rds.Redis8.SMembers(constant.PAYBLACKIPRANGE).Val() //fmt.Printf(" \n【PayBlackIPDuanCache configs】: %+v", blist) //PayBlackCache.Store(constant.PAYBLACKIPRANGE, blist) //return tools.InStringArray(ipDuan, blist) return tools.InStringArray(ipDuan, BlackIPDuan) } //GetBlackListVer2 黑名单新算法尝试 黑名单 A,B,C,D, E func GetBlackListVer2(userid, ip, UA string, totalmoney int, nickname string) (int, bool) { var ipDuan string ipArr := strings.Split(ip, ".") if len(ipArr) >= 3 { ipDuan = strings.Join(ipArr[0:3], ".") } uID, _ := strconv.Atoi(userid) if tools.InStringArray(ip, BlackIPs) { return 0, true } // 1. 根据ip段来判断如果是黑名单IP段, 用户直接拉入黑名单分区 if GetBlackIPDuanCache(ipDuan) { xlog.Infof("【 黑名单网段玩家 】 %v ip:%s agent:%v", userid, ip, UA) //获取用户信息 userInfo := gsdkclient.Client().UserInfoNoLoginData(uID) var count int temp := gsdkclient.Client().GetEffortEx(uint(uID), []int{2}, false) //获取局数 if temp != nil { count = int(temp[2]) //局数 } //CheckBlackAB 判断黑名单AB if typ, ok := CheckBlackABE(ip, userInfo.NickName, userInfo.RegDate, uID, 2, totalmoney, count); ok { return typ, ok } if totalmoney == 0 { //判断黑名单C 无特征,无支付 不影响审核人员判断依据,但是不开放支付功能 b := &BlackUsers{ Type: 3, Ip: ip, Count: count, Createtime: userInfo.RegDate, Nickname: userInfo.NickName, Totalmoney: totalmoney, Userid: uID, } b.Add() return b.Type, true } else { //黑名单D 无特征,有支付过玩家,不开放支付,进入审核名单没问题可以对外开放 b := &BlackUsers{ Type: 4, Ip: ip, Count: count, Createtime: userInfo.RegDate, Nickname: userInfo.NickName, Totalmoney: totalmoney, Userid: uID, } b.Add() return b.Type, true } } else { //非黑名单认证用户nick是不是审核人员 if typ, ok := CheckBlackA(ip, nickname, uID); ok { return typ, ok } } //判断是不是黑名单成员 如果是等级AB 记录他使用的IP, 等级CD只限制不统计IP //规则 A|182.254.115.151|局数:0|注册时间:2021-07-15 17:22:33.000000|昵称:WL_368879456|历史支付:10元 if typ := GetBlackLevel(uID); typ > 0 { if tools.InIntArray(typ, []int{1, 2}) { xlog.Infof("【 黑名单明确审核玩家切换IP 】 %v ip:%s agent:%v", userid, ip, UA) // rds.Redis8.HIncrBy(constant.PAYBLACKLEVELIP, ip, 1) //黑名单玩家计入使用该ip次数 return 0, true } xlog.Infof("【 黑名单不确定审核人员玩家切换IP 】 %v ip:%s agent:%v", userid, ip, UA) return 0, true } hour := time.Now().Hour() //高峰期不执行查询操作, 小于晚上8点,或者大于等于10点 进行特定IP认证 if hour < 20 || hour >= 23 { //判断用户IP是不是 黑名单用户使用过的IP countX := rds.Redis8.HGet(constant.PAYBLACKLEVELIP, ip).Val() if c, _ := strconv.Atoi(countX); c >= 5 { userInfo := gsdkclient.Client().UserInfoNoLoginData(uID) var count int temp := gsdkclient.Client().GetEffortEx(uint(uID), []int{2}, false) //获取局数 if temp != nil { count = int(temp[2]) //局数 } //CheckBlackAE 判断黑名单AE if typ, ok := CheckBlackABE(ip, userInfo.NickName, userInfo.RegDate, uID, 5, totalmoney, count); ok { xlog.Infof("【 通过黑名单IP进来的审核人员 】 %v ip:%s agent:%v", userid, ip, UA) return typ, ok } var day int regTime := strings.Replace(userInfo.RegDate, "T", " ", -1) if len(regTime) >= 19 { parse, _ := time.Parse("2006-01-02 15:04:05", regTime) day = CheckSubDay(parse) } //2个月未支付玩家,拉入黑名单C if day >= 60 && totalmoney == 0 { b := &BlackUsers{ Type: 3, Ip: ip, Count: count, Createtime: userInfo.RegDate, Nickname: userInfo.NickName, Totalmoney: totalmoney, Userid: uID, } b.Add() return b.Type, true } else { //用了黑名单用户ip, 但是充值过,或者只是近期没充值加入观察名单 r := &RedUsers{ Type: 1, Ip: ip, Count: count, Createtime: userInfo.RegDate, Nickname: userInfo.NickName, Totalmoney: totalmoney, Userid: uID, } r.Add() return 0, false } } } return 0, false } func CheckBlackA(ip, nickname string, userid int) (int, bool) { if len(nickname) > 7 { //判断是否符合黑名单A特征 含有关键字 if strings.HasPrefix(nickname, "tencent_game") || strings.HasPrefix(nickname, "rdgztest_") || strings.HasPrefix(nickname, "minigamecheck") { b := &BlackUsers{ Type: 1, Ip: ip, Count: 0, Createtime: "2022-01-01 00:00:00", Nickname: nickname, Totalmoney: 0, Userid: userid, } b.Add() return b.Type, true } } return 0, false } func CheckBlackABE(ip, nickname, reg string, userid, paytype, totalmoney, count int) (int, bool) { //判断是否符合黑名单A特征 含有关键字 if strings.Contains(nickname, "tencent_game") || strings.Contains(nickname, "rdgztest_") || strings.Contains(nickname, "minigamecheck") || strings.Contains(nickname, "city") { b := &BlackUsers{ Type: 1, Ip: ip, Count: count, Createtime: reg, Nickname: nickname, Totalmoney: totalmoney, Userid: userid, } b.Add() return b.Type, true } //判断黑名单B,E情况 局数0注册时间久 regTime := strings.Replace(reg, "T", " ", -1) if len(regTime) >= 19 { parse, _ := time.Parse("2006-01-02 15:04:05", regTime) if day := CheckSubDay(parse); day >= 180 && count == 0 { b := &BlackUsers{ Type: paytype, Ip: ip, Count: count, Createtime: reg, Nickname: nickname, Totalmoney: totalmoney, Userid: userid, } b.Add() return b.Type, true } } return 0, false } //GetBlackLevel 获取黑名单等级 func GetBlackLevel(userid int) int { if v, ok := PayBlackCacheNew.Get(userid); ok { return v } return -1 } //RedUsers 观察名单 type RedUsers struct { Id int `json:"id" sql:"id"` //自增ID Userid int `json:"userid" sql:"userid"` //用户ID Nickname string `json:"nickname" sql:"nickname"` //用户昵称 Count int `json:"count" sql:"count"` //局数 Totalmoney int `json:"total_money" sql:"total_money"` //支付历史金额 Ip string `json:"ip" sql:"ip"` //ip Createtime string `json:"create_time" sql:"create_time"` //注册时间 Time string `json:"time" sql:"time"` //创建时间 Type int `json:"type" sql:"type"` //等级 1观察名单 3 排除名单 } //BlackUsers 黑名单 type BlackUsers struct { Id int `json:"id" sql:"id"` //自增ID Userid int `json:"userid" sql:"userid"` //用户ID Nickname string `json:"nickname" sql:"nickname"` //用户昵称 Count int `json:"count" sql:"count"` //局数 Totalmoney int `json:"total_money" sql:"total_money"` //支付历史金额 Ip string `json:"ip" sql:"ip"` //ip Createtime string `json:"create_time" sql:"create_time"` //注册时间 Time string `json:"time" sql:"time"` //创建时间 Type int `json:"type" sql:"type"` //等级 1(关键字) 2(无游戏) 3(无支付) 4(IP段其他) 5(单IP无游戏) } func (b *BlackUsers) Add() (int64, error) { var last int64 var err error sqlQuery := fmt.Sprintf("INSERT IGNORE INTO black_users (`userid`,`nickname`,`count`,`total_money`,`ip`, `create_time`, `type`) VALUE(?,?,?,?,?,?,?)") last, err = dbx.MySQL.Insert(sqlQuery, b.Userid, b.Nickname, b.Count, b.Totalmoney, b.Ip, strings.Replace(b.Createtime, "T", " ", -1), b.Type) if err != nil { logs.Error(fmt.Sprintf("black_users param:%#v err:%v", b, err)) return 0, err } return last, nil } func GetBlackUsersType(userid int) int { var typ int err := dbx.MySQL.QueryRow("SELECT type from `black_users` where userid = ? ", userid).Scan(&typ) if nil != err { if err == sql.ErrNoRows { return typ } logs.Errorf("GetBlackUsersType err:%v", err) } return typ } func GetBlackAlLUsers() { PayBlackCacheNew = NewPayBlackCacheNew() t1 := time.Now() rows, err := dbx.MySQL.Query("SELECT userid, type from `black_users` where type in (1,2)") defer rows.Close() if err != nil && err != sql.ErrNoRows { logs.Errorf("GetBlackAlLUsers err:%v", err) return } var userid, typ int for rows.Next() { err = rows.Scan(&userid, &typ) if err != nil { logs.Errorf("GetBlackAlLUsers err:%v", err) return } PayBlackCacheNew.Set(userid, typ) } t2 := time.Now() xlog.Infof("加载黑名单配置时长:%s 数据:%+v", t2.Sub(t1), PayBlackCacheNew) } func (b *RedUsers) Add() (int64, error) { var last int64 var err error sqlQuery := "INSERT IGNORE INTO red_users (`userid`,`nickname`,`count`,`total_money`,`ip`, `create_time`, `type`) VALUE(?,?,?,?,?,?,?)" last, err = dbx.MySQL.Insert(sqlQuery, b.Userid, b.Nickname, b.Count, b.Totalmoney, b.Ip, strings.Replace(b.Createtime, "T", " ", -1), b.Type) if err != nil { logs.Error(fmt.Sprintf("red_users param:%#v err:%v", b, err)) return 0, err } return last, nil } func GetBUserInfo(tag string, st int64, level string) []BlackUsers { start := tools.TimeParseForMat(st, "2006-01-02 15:04:05") end := tools.TimeParseForMat(st+86400, "2006-01-02 15:04:05") var table, where string if tag == "red" { table = "red_users" } else if tag == "black" { table = "black_users" } else { return nil } if level != "" { l, _ := strconv.Atoi(level) where = fmt.Sprintf(" and type = %d", l) } var bu []BlackUsers sql := fmt.Sprintf("select * from `%s` where time >= '%s' and time <= '%s' %s ORDER BY time DESC", table, start, end, where) dbx.MySQL.Query2(sql, &bu) return bu }