diff --git a/cron/cron.go b/cron/cron.go index 6acb656d97c4a059262038cf8fe915b098331f20..985e3035738eadb4e4bcdf7ec924dba608481671 100644 --- a/cron/cron.go +++ b/cron/cron.go @@ -19,7 +19,8 @@ func Init() { group_cron.GroupPowerExpClear() // 清理家族经验/等级 group_cron.GroupPowerMonthRankAct() //group_cron.GroupInEventInit() // 进房事件 - group_cron.CreateGroup() // + //group_cron.CreateGroup() // group_cron.CalcGroupSupport() // 群组扶持计算 //group_cron.CalcGroupSupport_OldData() + group_cron.GroupCountryListSort() } diff --git a/cron/gift_cron/send_gift_redis.go b/cron/gift_cron/send_gift_redis.go index 475927e31e89261aa4fc8865788e05bc68835409..35f6c557586fdb421e72fdc617c412238ed07335 100644 --- a/cron/gift_cron/send_gift_redis.go +++ b/cron/gift_cron/send_gift_redis.go @@ -105,7 +105,7 @@ func groupSupportAddConsume(model *domain.Model, sendGiftEvent *gift_ev.SendGift } _, _, period := group_m.GetSupportLevelTime(time.Now()) // 钻石数 - diamond := sendGiftEvent.GiftN * sendGiftEvent.ResGift.DiamondNum + diamond := sendGiftEvent.GiftN * sendGiftEvent.ResGift.DiamondNum * mysql.Num(len(sendGiftEvent.ReceiveUserIds)) keyDiamond := rediskey.GetGroupSupportConsumeSummary(period) _, err := model.RedisCluster.ZIncrBy(context.Background(), keyDiamond, float64(diamond), sendGiftEvent.SceneUid).Result() if err != nil { diff --git a/cron/group_cron/group_list.go b/cron/group_cron/group_list.go new file mode 100644 index 0000000000000000000000000000000000000000..c6175f297675c30015eea6e88d96e973b9696792 --- /dev/null +++ b/cron/group_cron/group_list.go @@ -0,0 +1,32 @@ +package group_cron + +import ( + "git.hilo.cn/hilo-common/domain" + "github.com/robfig/cron" + "hilo-group/domain/service/group_s" +) + +// 清理家族经验和等级 +func GroupCountryListSort() { + + // 常用国家-每15分钟计算国家房间列表排序 /v1/imGroup/country [get] 接口 + spec := "0 */15 * * * ?" + c := cron.New() + _ = c.AddFunc(spec, func() { + var model = domain.CreateModelNil() + model.Log.Infof("GroupCountryListSort Common start") + group_s.SortGroupCommonCountryList(model) + model.Log.Infof("GroupCountryListSort Common end") + }) + + // 非常用国家-每60分钟计算国家房间列表排序 /v1/imGroup/country [get] 接口 + spec2 := "0 46 * * * ?" + _ = c.AddFunc(spec2, func() { + var model = domain.CreateModelNil() + model.Log.Infof("GroupCountryListSort not Common start") + group_s.SortGroupNotCommonCountryList(model) + model.Log.Infof("GroupCountryListSort not Common end") + }) + + c.Start() +} diff --git a/domain/model/group_m/mic.go b/domain/model/group_m/mic.go index 20ba9607713c6bda669bbc9feba19e359ce0290e..3dce551a1125e6839a7a71460cd3ac4c48080a8a 100644 --- a/domain/model/group_m/mic.go +++ b/domain/model/group_m/mic.go @@ -920,6 +920,40 @@ func GetMicHasInGroupNum(model *domain.Model) (map[string]int64, error) { return resultGroupUuids, nil } +// 获取麦上人数 +func GetMicHasInGroupNumByList(model *domain.Model, groupIds []string) (map[string]int64, error) { + resultGroupUuids := make(map[string]int64, len(groupIds)) + micHasInKey := redis_key.GetPrefixGroupMicHasIn() + for _, group := range groupIds { + isMem, err := redisCli.GetRedis().SIsMember(context.Background(), micHasInKey, group).Result() + if err != nil { + model.Log.Errorf("GetMicHasInGroupNumByList err:%v", err) + return nil, myerr.WrapErr(err) + } + if isMem { + s := strings.Replace(micHasInScript, "{key}", micHasInKey, -1) + s = strings.Replace(s, "{remKey}", group, -1) + for i := 1; i <= MaxMicNum; i++ { + s = strings.Replace(s, "{key"+strconv.Itoa(i)+"}", redis_key.GetPrefixGroupMicUser(group, i), -1) + } + //r, err := redis2.NewScript(s).Run(context.Background(), redisCli.GetRedis(), []string{}).Result() + sha1, err := model.Redis.ScriptLoad(model, s).Result() + if err != nil { + return nil, myerr.WrapErr(err) + } + micNum, err := model.Redis.EvalSha(model, sha1, nil, nil).Int64() + if err != nil { + return nil, myerr.WrapErr(err) + } + //d := r.(int64) + if micNum > 0 { + resultGroupUuids[group] = micNum + } + } + } + return resultGroupUuids, nil +} + //获取麦上有人的群组&&麦上的数(有时间性,目前是24小时) func GetMicHasInPeriodGroupUser() (map[string][]uint64, error) { //清理超过12小时的 diff --git a/domain/model/res_m/country.go b/domain/model/res_m/country.go index b139ad9fb6d09125a84d49af2c19344ceae03b39..6d23b29250f6b4bc8c9f9d970c63702ca866550b 100644 --- a/domain/model/res_m/country.go +++ b/domain/model/res_m/country.go @@ -213,3 +213,10 @@ func GetLangeByCountry(db *gorm.DB, country mysql.Str) (string, error) { return "", myerr.WrapErr(err) } } + +//获取所有国家名字列表 +func GetCountryNameList(model *domain.Model) ([]string, error) { + res := make([]string, 0) + err := model.DB().Select("distinct(name) name").Pluck("name", &res).Error + return res, myerr.WrapErr(err) +} diff --git a/domain/service/group_s/group_list.go b/domain/service/group_s/group_list.go new file mode 100644 index 0000000000000000000000000000000000000000..fe6a4165dfeaa3af7993bc45b29e42e983281b93 --- /dev/null +++ b/domain/service/group_s/group_list.go @@ -0,0 +1,245 @@ +package group_s + +import ( + "context" + "git.hilo.cn/hilo-common/_const/rediskey" + "git.hilo.cn/hilo-common/domain" + "git.hilo.cn/hilo-common/resource/redisCli" + "github.com/go-redis/redis/v8" + "hilo-group/_const/enum/gift_e" + "hilo-group/cv/gift_cv" + "hilo-group/domain/cache/room_c" + "hilo-group/domain/model/group_m" + "hilo-group/domain/model/res_m" + "sort" + "strconv" + "time" +) + +func SortGroupCommonCountryList(model *domain.Model) { + // 常用的国家 + countryMap := map[string]struct{}{"India": {}, "Indonesia": {}, "Iraq": {}, "KSA": {}, "Kuwait": {}, "Pakistan": {}, "Turkey": {}} + for country, _ := range countryMap { + sortGroupList, err := GetGroupSortList(model, country) + if err != nil { + model.Log.Errorf("SortGroupCommonCountryList err:%v", err) + return + } + // 写入redis + err = setToRedis(model, country, sortGroupList) + if err != nil { + model.Log.Errorf("SortGroupCommonCountryList country:%v, len(sortGroupList):%v, err:%v", country, len(sortGroupList), err) + return + } + time.Sleep(time.Second * 3) + } +} + +func SortGroupNotCommonCountryList(model *domain.Model) { + // 常用的国家 + countryMap := map[string]struct{}{"India": {}, "Indonesia": {}, "Iraq": {}, "KSA": {}, "Kuwait": {}, "Pakistan": {}, "Turkey": {}} + // 取所有的国家名字 + allCountryList, err := res_m.GetCountryNameList(model) + if err != nil { + model.Log.Errorf("SortGroupNotCommonCountryList err:%v", err) + return + } + for _, country := range allCountryList { + if _, ok := countryMap[country]; ok { + continue + } + // 计算非常用国家 + sortGroupList, err := GetGroupSortList(model, country) + if err != nil { + model.Log.Errorf("SortGroupNotCommonCountryList err:%v", err) + return + } + // 写入redis + err = setToRedis(model, country, sortGroupList) + if err != nil { + model.Log.Errorf("SortGroupNotCommonCountryList country:%v, len(sortGroupList):%v, err:%v", country, len(sortGroupList), err) + return + } + time.Sleep(time.Second * 5) + } +} + +func setToRedis(model *domain.Model, country string, groupList []string) error { + // 写入redis + key := rediskey.GetGroupCountrySortList(country) + for idx, group := range groupList { + err := redisCli.GetRedis().ZRemRangeByRank(context.Background(), key, int64(idx), int64(idx)).Err() // 先删除旧的 + if err != nil { + model.Log.Errorf("setToRedis SortGroup key:%v, idx:%v, err:%v", key, idx, err) + return err + } + // 插入 + err = redisCli.GetRedis().ZAdd(context.Background(), key, &redis.Z{Score: float64(idx), Member: group}).Err() + if err != nil { + model.Log.Errorf("setToRedis SortGroup key:%v, idx:%v, group:%s, err:%v", key, idx, group, err) + return err + } + if idx%1000 == 0 { + time.Sleep(time.Millisecond * 50) + } + } + return nil +} + +// 计算国家群组列表排序 +func GetGroupSortList(model *domain.Model, country string) ([]string, error) { + bannedGroups, err := group_m.GetBannedGroupsMap(model) + if err != nil { + return nil, err + } + beginTime := time.Now() + groups, banCount, visitCount, err := GetCandidatesByCountry(model, bannedGroups, country) + if err != nil { + return nil, err + } + endTime := time.Now() + model.Log.Infof("GroupCountryListSort: candidates size = %d, takes %d ms banned = %d, visitCount size = %d", + len(groups), endTime.Sub(beginTime).Milliseconds(), banCount, len(visitCount)) + + // 获取麦上有人的所有群组及麦上人数 + micGroupNum, err := group_m.GetMicHasInGroupNum(model) + if err != nil { + return nil, err + } + model.Log.Infof("GroupCountryListSort, micGroupNum : %v", micGroupNum) + model.Log.Infof("GroupCountryListSort cost2:%v", time.Now().Sub(beginTime)) + + sortedGroupIds := make([]string, 0) + diamondGroupIds := make([]string, 0) + for i, _ := range groups { + // 麦上没人也放出来 + sortedGroupIds = append(sortedGroupIds, i) + // 麦上有人才计算流水 + if micGroupNum[i] > 0 { + diamondGroupIds = append(diamondGroupIds, i) + } + } + + now := time.Now() + bTime := now.Add(-time.Minute * 30) + g := gift_cv.GiftOperate{SceneType: gift_e.GroupSceneType} + diamonds, err := g.GetRangeConsumeSummaryV2(bTime, now, diamondGroupIds) + if err != nil { + return nil, err + } + model.Log.Infof("GroupCountryListSort, diamonds in 30 mins: %v", diamonds) + model.Log.Infof("GroupCountryListSort cost3:%v", time.Now().Sub(beginTime)) + + supportLevels, err := NewGroupService(model.MyContext).GetWeekMaxSupportLevelMap() + if err != nil { + return nil, err + } + model.Log.Infof("GroupCountryListSort, supportLevels : %v", supportLevels) + model.Log.Infof("GroupCountryListSort cost4:%v", time.Now().Sub(beginTime)) + + // 排序优先级2022-07-25 + sort.Slice(sortedGroupIds, func(i, j int) bool { + gi := sortedGroupIds[i] + gj := sortedGroupIds[j] + + // 1、按麦上人数多少排序 + if micGroupNum[gi] > micGroupNum[gj] { + return true + } else if micGroupNum[gi] < micGroupNum[gj] { + return false + } + + // 2、麦上人数相同,按30分钟内送礼钻石数排序 + if diamonds[gi] > diamonds[gj] { + return true + } else if diamonds[gi] < diamonds[gj] { + return false + } + // 3. 根据热度排序groupInUserDuration + if visitCount[gi] > visitCount[gj] { + return true + } else if visitCount[gi] < visitCount[gj] { + return false + } + + // * Final resort: 群组CODE,短号优先,然后按字母序 + return len(groups[gi].Code) < len(groups[gj].Code) || len(groups[gi].Code) == len(groups[gj].Code) && groups[gi].Code < groups[gj].Code + }) + model.Log.Infof("GroupCountryListSort cost5:%v", time.Now().Sub(beginTime)) + return sortedGroupIds, nil +} + +// 国家群候选:没有密码且没被封禁的群, 有国家 +func GetCandidatesByCountry(model *domain.Model, bannedGroups map[string]uint64, country string) (map[string]*group_m.GroupInfo, int, map[string]int64, error) { + noPwdGroups, err := group_m.FindOwnerCountryGroups(model, country) + if err != nil { + return nil, 0, nil, err + } + var groupIds []string + for _, v := range noPwdGroups { + groupIds = append(groupIds, v.ImGroupId) // imGroupId + } + + //roomVisitCount, err := room_c.GetAllRoomVisitCount() + roomVisitCount, err := room_c.MGetRoomVisitCount(groupIds) + if err != nil { + return nil, 0, nil, err + } + gameRoom := group_m.GetGameGroupsMap(model) + + banCount := 0 + groups := make(map[string]*group_m.GroupInfo, 0) + visitCount := make(map[string]int64) + for i, v := range noPwdGroups { + // 过滤掉被封禁的群 + if bannedGroups[v.ImGroupId] != 0 { + banCount++ + continue + } + // 过滤游戏房 + if gameRoom[v.ImGroupId] { + continue + } + + // 先从二级缓存中找 + if c, ok := roomVisitCount[v.ImGroupId]; ok { + if vc, err := strconv.ParseInt(c, 10, 64); err == nil && vc > 0 { + //model.Log.Debugf("getPopularCandidates, from roomVisitCount %s(%s) - %d", v.ImGroupId, v.Code, vc) + + groups[v.ImGroupId] = &noPwdGroups[i] + visitCount[v.ImGroupId] = vc + } + } else { + // 如果没有,就从roomVisit中取 + if vc, err := room_c.GetSetRoomVisitCount(v.ImGroupId); err == nil && vc > 0 { + model.Log.Infof("getPopularCandidates, from roomVisit %s(%s) - %d", v.ImGroupId, v.Code, vc) + + groups[v.ImGroupId] = &noPwdGroups[i] + visitCount[v.ImGroupId] = vc + } + } + } + return groups, banCount, visitCount, nil +} + +func GetVisitCount(groupIds []string) (map[string]int64, error) { + roomVisitCount, err := room_c.MGetRoomVisitCount(groupIds) + if err != nil { + return nil, err + } + visitCount := make(map[string]int64) + for _, v := range groupIds { + // 先从二级缓存中找 + if c, ok := roomVisitCount[v]; ok { + if vc, err := strconv.ParseInt(c, 10, 64); err == nil && vc > 0 { + visitCount[v] = vc + } + } else { + // 如果没有,就从roomVisit中取 + if vc, err := room_c.GetSetRoomVisitCount(v); err == nil && vc > 0 { + visitCount[v] = vc + } + } + } + return visitCount, nil +} diff --git a/route/group_r/group_list.go b/route/group_r/group_list.go index a8e2300a3845def304fb1b58a4bef50b4b1c6835..3b8706cd499fcf0f346bd8ffc008c1c40d81edfc 100644 --- a/route/group_r/group_list.go +++ b/route/group_r/group_list.go @@ -1,10 +1,13 @@ package group_r import ( + "context" "fmt" + "git.hilo.cn/hilo-common/_const/rediskey" "git.hilo.cn/hilo-common/domain" "git.hilo.cn/hilo-common/mycontext" "git.hilo.cn/hilo-common/resource/mysql" + "git.hilo.cn/hilo-common/resource/redisCli" "git.hilo.cn/hilo-common/rpc" "git.hilo.cn/hilo-common/utils" "github.com/gin-gonic/gin" @@ -18,7 +21,6 @@ import ( "hilo-group/cv/user_cv" "hilo-group/domain/cache/group_c" "hilo-group/domain/cache/res_c" - "hilo-group/domain/cache/room_c" "hilo-group/domain/model/game_m" "hilo-group/domain/model/group_m" "hilo-group/domain/model/noble_m" @@ -1251,7 +1253,7 @@ func GetGroupByCountry(c *gin.Context) (*mycontext.MyContext, error) { //model.Log.Infof("GetGroupByCountry: page size = %d, page index = %d, banMap %v", pageSize, pageIndex, bannedGroups) beginTime := time.Now() - groups, banCount, visitCount, err := getCandidatesByCountry(model, bannedGroups, countryShortName) + groups, banCount, visitCount, err := group_s.GetCandidatesByCountry(model, bannedGroups, countryShortName) if err != nil { return myContext, err } @@ -1461,57 +1463,180 @@ func GetGroupByCountry(c *gin.Context) (*mycontext.MyContext, error) { return myContext, nil } -// 国家群候选:没有密码且没被封禁的群, 有国家 -func getCandidatesByCountry(model *domain.Model, bannedGroups map[string]uint64, country string) (map[string]*group_m.GroupInfo, int, map[string]int64, error) { - noPwdGroups, err := group_m.FindOwnerCountryGroups(model, country) +func GetGroupByCountryV2(c *gin.Context) (*mycontext.MyContext, error) { + myContext := mycontext.CreateMyContext(c.Keys) + myUserId, err := req.GetUserId(c) if err != nil { - return nil, 0, nil, err + return myContext, err } - var groupIds []string - for _, v := range noPwdGroups { - groupIds = append(groupIds, v.ImGroupId) // imGroupId + + countryShortName := c.Query("countryShortName") + if countryShortName == "" { + return myContext, myerr.NewSysError("countryShortName 为必填项") + } + pageSize, err := strconv.Atoi(c.Query("pageSize")) + if err != nil || pageSize <= 0 { + pageSize = 10 + } + pageIndex, err := strconv.Atoi(c.Query("pageIndex")) + if err != nil || pageIndex <= 0 { + pageIndex = 1 } - //roomVisitCount, err := room_c.GetAllRoomVisitCount() - roomVisitCount, err := room_c.MGetRoomVisitCount(groupIds) + model := domain.CreateModelContext(myContext) + + // 从redis分页取群组id + sortedGroupIds := make([]string, 0, pageSize) + beginPos := int64(pageSize * (pageIndex - 1)) + endPos := int64(pageSize*pageIndex - 1) + key := rediskey.GetGroupCountrySortList(countryShortName) + zList, err := redisCli.GetRedis().ZRangeWithScores(context.Background(), key, beginPos, endPos).Result() if err != nil { - return nil, 0, nil, err + model.Log.Errorf("GetGroupByCountry err:%v", err) + return myContext, err + } + for _, v := range zList { + sortedGroupIds = append(sortedGroupIds, v.Member.(string)) } - gameRoom := group_m.GetGameGroupsMap(model) - banCount := 0 - groups := make(map[string]*group_m.GroupInfo, 0) - visitCount := make(map[string]int64) - for i, v := range noPwdGroups { - // 过滤掉被封禁的群 - if bannedGroups[v.ImGroupId] != 0 { - banCount++ - continue - } - // 过滤游戏房 - if gameRoom[v.ImGroupId] { - continue + hotGroupList := make([]*group_m.GroupInfo, 0) + // * 同语言区 ^ 麦上有人 + 开放群 - 需要等级的群 + for _, i := range sortedGroupIds { + gInfo, err := group_m.GetGroupInfo(model, i) + if err != nil { + model.Log.Errorf("GetGroupByCountry err:%v", err) + return myContext, err } + hotGroupList = append(hotGroupList, gInfo) + } + total, err := redisCli.GetRedis().ZCard(context.Background(), key).Uint64() + if err != nil { + model.Log.Errorf("GetGroupByCountry err:%v", err) + return myContext, err + } - // 先从二级缓存中找 - if c, ok := roomVisitCount[v.ImGroupId]; ok { - if vc, err := strconv.ParseInt(c, 10, 64); err == nil && vc > 0 { - //model.Log.Debugf("getPopularCandidates, from roomVisitCount %s(%s) - %d", v.ImGroupId, v.Code, vc) + // 返回结果 + result := make([]group_cv.PopularGroupInfo, 0) - groups[v.ImGroupId] = &noPwdGroups[i] - visitCount[v.ImGroupId] = vc - } - } else { - // 如果没有,就从roomVisit中取 - if vc, err := room_c.GetSetRoomVisitCount(v.ImGroupId); err == nil && vc > 0 { - model.Log.Infof("getPopularCandidates, from roomVisit %s(%s) - %d", v.ImGroupId, v.Code, vc) + groupIds := make([]string, 0) + owners := make([]uint64, 0) + for _, i := range hotGroupList { + groupIds = append(groupIds, i.ImGroupId) + owners = append(owners, i.Owner) + } + powerIds, powerNames, powerNameplates, powerGrades, err := group_power_cv.BatchGetGroupPower(model.Db, owners) + if err != nil { + return myContext, err + } + groupMedals, err := group_m.BatchGetMedals(model.Db, groupIds) + if err != nil { + return myContext, err + } + resMedal, err := res_m.MedalGetAllMap(model.Db) + if err != nil { + return myContext, err + } + + countryInfo, err := res_c.GetCountryIconMap(model) + if err != nil { + return myContext, err + } - groups[v.ImGroupId] = &noPwdGroups[i] - visitCount[v.ImGroupId] = vc + rr := rocket_m.RocketResult{} + maxStageMap, err := rr.GetMaxStage(model.DB(), groupIds) + if err != nil { + return myContext, err + } + + roomCount, err := group_m.BatchGetRoomCount(model, groupIds) + if err != nil { + return nil, err + } + // 正在进行的游戏 + games := game_m.GetNotEndGamesMap(model) + // 扶持等级 + supportLevels, err := group_s.NewGroupService(myContext).GetWeekMaxSupportLevelMap() + if err != nil { + return myContext, err + } + visitCount, err := group_s.GetVisitCount(groupIds) + if err != nil { + model.Log.Errorf("GetGroupByCountry err:%v", err) + return myContext, err + } + // 获取麦上人数 + micGroupNum, err := group_m.GetMicHasInGroupNum(model) + if err != nil { + return myContext, err + } + + for _, i := range hotGroupList { + var maxStage *uint16 = nil + if s, ok := maxStageMap[i.ImGroupId]; ok { + maxStage = &s + } + + medals := make([]medal_cv.PicElement, 0) + if m, ok := groupMedals[i.ImGroupId]; ok { + for _, j := range m { + mId := uint32(j) + if e, ok := resMedal[mId]; ok { + medals = append(medals, medal_cv.PicElement{ + PicUrl: e.PicUrl, + }) + } } } + // 补上房间流水勋章 + var pe *medal_cv.PicElement + _, pe, err = medal_cv.GetGroupConsumeMedal(model, i.ImGroupId) + if err != nil { + model.Log.Infof("GetGroupByCountry: GetGroupConsumeMedal: %s", err.Error()) + } else if pe != nil { + medals = append(medals, medal_cv.PicElement{PicUrl: pe.PicUrl}) + } + + var password *string = nil + if len(i.Password) > 0 && i.Owner != myUserId { + emptyStr := "" + password = &emptyStr + } + result = append(result, group_cv.PopularGroupInfo{ + GroupInfo: group_cv.GroupInfo{ + GroupBasicInfo: group_cv.GroupBasicInfo{ + GroupId: i.TxGroupId, + Name: i.Name, + Introduction: i.Introduction, + Notification: i.Notification, + FaceUrl: i.FaceUrl, + Code: i.Code, + CountryIcon: countryInfo[i.Country], + Password: password, + SupportLevel: supportLevels[i.ImGroupId], + GroupInUserDuration: visitCount[i.ImGroupId], + MicNumType: int(i.MicNumType), + GroupMedals: medals, + }, + HasOnMic: micGroupNum[i.ImGroupId] > 0, + GroupPowerId: powerIds[i.Owner], + GroupPowerName: powerNames[i.Owner], + GroupPowerNameplate: powerNameplates[i.Owner], + GroupPower: group_cv.GroupPower{ + Id: powerIds[i.Owner], + Name: powerNames[i.Owner], + Nameplate: powerNameplates[i.Owner], + Grade: powerGrades[i.Owner], + }, + }, + MicUsers: []user_cv.CvUserTiny{}, + RoomUserCount: uint(roomCount[i.ImGroupId]), + MaxStage: maxStage, + GameTypes: games[i.TxGroupId], + }) } - return groups, banCount, visitCount, nil + + resp.ResponsePageOk(c, result, uint(total), pageIndex) + return myContext, nil } // @Tags 资源