package group_r import ( "fmt" "git.hilo.cn/hilo-common/domain" "git.hilo.cn/hilo-common/mycontext" "git.hilo.cn/hilo-common/resource/mysql" "git.hilo.cn/hilo-common/rpc" "git.hilo.cn/hilo-common/utils" "github.com/gin-gonic/gin" "hilo-group/_const/enum/gift_e" "hilo-group/_const/enum/group_e" "hilo-group/_const/enum/online_e" "hilo-group/cv/gift_cv" "hilo-group/cv/group_cv" "hilo-group/cv/group_power_cv" "hilo-group/cv/medal_cv" "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" "hilo-group/domain/model/res_m" "hilo-group/domain/model/rocket_m" "hilo-group/domain/model/tim_m" "hilo-group/domain/model/user_m" "hilo-group/domain/service/group_s" "hilo-group/myerr" "hilo-group/myerr/bizerr" "hilo-group/req" "hilo-group/resp" "sort" "strconv" "time" ) // @Tags 群组 // @Summary 查询热门群组 // @Accept application/x-www-form-urlencoded // @Param token header string true "token" // @Param nonce header string true "随机数字" // @Param pageSize query int false "分页大小 默认:10" default(10) // @Param pageIndex query int false "第几个分页,从1开始 默认:1" default(1) // @Success 200 {object} group_cv.PopularGroupInfo // @Router /v1/imGroup/popular [get] func GetPopularGroups(c *gin.Context) (*mycontext.MyContext, error) { start := time.Now() myContext := mycontext.CreateMyContext(c.Keys) // ios 贵族6不兼容需要强更 deviceType := c.GetHeader(mycontext.DEVICETYPE) appVersion := c.GetHeader(mycontext.APP_VERSION) if deviceType == "iOS" { if low, _ := utils.CompareVersion(appVersion, "< 3.7.0"); low { return myContext, bizerr.UpgradeRequired } } 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 } myUserId, _, _, _, myCountry, err := req.GetUserEx(c, myContext) if err != nil { return myContext, err } model := domain.CreateModelContext(myContext) // 获取国家信息 _, area, err := user_m.GetUserCountryArea(model, myUserId) if err != nil { model.Log.Errorf("GetUserCountryArea 获取国家资源错误 userId:%d, err:%v", myUserId, err) return myContext, err } /* 2022-06-30 老板说先不分区 regions, err := res_m.GetAllLangRegion(model) if err != nil { return myContext, err } myRegion := regions[myCountry] model.Log.Infof("GetPopularGroups: user %d, name = %s, country = %s, region = %s", myUserId, myNick, myCountry, myRegion) */ bannedGroups, err := group_m.GetBannedGroupsMap(model) if err != nil { return myContext, err } gs := group_m.GroupSetting{IsHidden: true} hiddenGroups, _ := gs.GetHidden(model.Db) if err != nil { return myContext, err } gameGroups := group_m.GetGameGroupsMap(model) model.Log.Infof("GetPopularGroups: page size = %d, page index = %d, banMap %v, hidenMap %v,cost:%v", pageSize, pageIndex, bannedGroups, hiddenGroups, time.Now().Sub(start)) hotGroupList := make([]group_m.GroupInfo, 0) // 获取麦上有人的所有群组及麦上人数 v10,只有麦上有人的能上热门 micGroupNum, err := group_m.GetMicHasInGroupNum(model) if err != nil { return myContext, err } model.Log.Infof("GetMicHasInGroupNum: cost %v", time.Now().Sub(start)) banCount := 0 hiddenCount := 0 groupIds := make([]string, 0) for i := range micGroupNum { // 过滤掉被封禁的群 if bannedGroups[i] != 0 { banCount++ continue } // 过滤掉被隐藏的群 if _, exist := hiddenGroups[i]; exist { hiddenCount++ continue } // 过滤掉游戏房 if gameGroups[i] { continue } groupIds = append(groupIds, i) } model.Log.Infof("GetPopularGroups, micGroupNum: %v, banned %d, hidden %d,cost:%v", micGroupNum, banCount, hiddenCount, time.Now().Sub(start)) // 3. 处理置顶群 topGroupIds, err := getTopGroups(model, bannedGroups, hiddenGroups, area) if err != nil { return myContext, err } sortedGroupIds := make([]string, 0) for _, i := range groupIds { found := false for _, j := range topGroupIds { if i == j { found = true break } } // 已经在置顶群范围内的跳过 if !found { sortedGroupIds = append(sortedGroupIds, i) } } groupIds = append(groupIds, topGroupIds...) groups, err := group_m.BatchGetGroupInfo(model, groupIds) if err != nil { return myContext, err } for _, i := range topGroupIds { /* 2022-06-30 老板说先不分区 // 置顶只对同语言区的生效 if myRegion != regions[topGroupInfo[i].Country] { continue } */ // 已经置顶的,直接进队列,不再参与排序 hotGroupList = append(hotGroupList, groups[i]) //delete(groupIds, i) } // for pretty log //logstr := "" //for _, i := range hotGroupList { //logstr += " " + i.ImGroupId //} //logstr += " |" myArea := fmt.Sprintf("%d", area) // 国家区域信息 resAreaMap, err := res_c.GetCountryAreaMap(model) if err != nil { return myContext, err } areaScore := make(map[string]int) countryScore := make(map[string]int) if len(myCountry) > 0 { for _, i := range sortedGroupIds { if myCountry == groups[i].Country { countryScore[i] = 1 } else { countryScore[i] = 0 } if cArea, ok := resAreaMap[groups[i].Country]; ok { if myArea == cArea { areaScore[i] = 1 } else { areaScore[i] = 0 } } } } model.Log.Infof("GetPopularGroups, countryScore[*]: %v,cost:%v", countryScore, time.Now().Sub(start)) now := time.Now() bTime := now.Add(-time.Minute * 30) g := gift_cv.GiftOperate{SceneType: gift_e.GroupSceneType} diamonds, err := g.GetRangeConsumeSummaryV2(bTime, now, groupIds) if err != nil { return myContext, err } model.Log.Infof("GetPopularGroups, diamonds in 30 mins: %v,cost:%v", diamonds, time.Now().Sub(start)) visitCount, err := group_m.BatchGetRoomVisitCount(model.Log, groupIds) if err != nil { return myContext, err } // 排序优先级2022-07-25版本 sort.Slice(sortedGroupIds, func(i, j int) bool { gi := sortedGroupIds[i] gj := sortedGroupIds[j] // 1、老板说,优化按国家 if countryScore[gi] > countryScore[gj] { return true } else if countryScore[gi] < countryScore[gj] { return false } // 不是我的国家,按区域排序 if countryScore[gi] == 0 { if areaScore[gi] > areaScore[gj] { return true } else if areaScore[gi] < areaScore[gj] { return false } } // 2、按麦上人数多少排序 if micGroupNum[gi] > micGroupNum[gj] { return true } else if micGroupNum[gi] < micGroupNum[gj] { return false } // 3、麦上人数相同,按30分钟内送礼钻石数排序 if diamonds[gi] > diamonds[gj] { return true } else if diamonds[gi] < diamonds[gj] { return false } // 4、按热度递减排序 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 }) // for pretty log // * 同国家 ^ 麦上有人 + 开放群 - 需要等级的群 for _, g := range sortedGroupIds { hotGroupList = append(hotGroupList, groups[g]) //prefix := " " //if countryScore[g] == 0 { //prefix += "*" //} //logstr += prefix + g + ":" + groups[g].Code + ":" + strconv.Itoa(int(micGroupNum[g])) + // ":" + strconv.FormatUint(diamonds[g], 10) + ":" + strconv.Itoa(int(visitCount[g])) } total := len(hotGroupList) model.Log.Infof("GetPopularGroups: hotGroupList size = %d,cost:%v", total, time.Now().Sub(start)) result := make([]group_cv.PopularGroupInfo, 0) beginPos := pageSize * (pageIndex - 1) endPos := pageSize * pageIndex if beginPos < total { if endPos > total { endPos = total } groupIds := make([]string, 0) owners := make([]uint64, 0) for _, i := range hotGroupList[beginPos:endPos] { 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 } //roomMicUserMap, err := group_m.BatchGetAllMicUser(model, groupIds) //if err != nil { // return myContext, err //} model.Log.Infof("GetPopularGroups: final start = %d, end = %d, groupIds %v,cost:%v", beginPos, endPos, groupIds, time.Now().Sub(start)) roomCount, err := group_m.BatchGetRoomCount(model, groupIds) if err != nil { return nil, err } countryInfo, err := res_c.GetCountryIconMap(model) if err != nil { return myContext, err } supportLevels, err := group_s.NewGroupService(myContext).GetWeekMaxSupportLevelMap() if err != nil { return myContext, err } rr := rocket_m.RocketResult{} maxStageMap, err := rr.GetMaxStage(mysql.Db, groupIds) if err != nil { return myContext, err } // 正在进行的游戏 games := game_m.GetNotEndGamesMap(model) for _, i := range hotGroupList[beginPos:endPos] { micUsers := make([]user_cv.CvUserTiny, 0) var maxStage *uint16 = nil if s, ok := maxStageMap[i.ImGroupId]; ok { maxStage = &s } medals := make([]medal_cv.PicElement, 0) // 补上房间流水勋章 var pe *medal_cv.PicElement _, pe, err = medal_cv.GetGroupConsumeMedal(model, i.ImGroupId) if err != nil { model.Log.Infof("GetPopularGroups: GetGroupConsumeMedal: %s", err.Error()) } else if pe != nil { medals = append(medals, medal_cv.PicElement{PicUrl: pe.PicUrl}) } 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 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: true, 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: micUsers, RoomUserCount: uint(roomCount[i.ImGroupId]), MaxStage: maxStage, GameTypes: games[i.TxGroupId], }) } } resp.ResponsePageOk(c, result, uint(total), pageIndex) return myContext, nil } func getTopGroups(model *domain.Model, bannedGroups map[string]uint64, hiddenGroups map[string]struct{}, area int) ([]string, error) { topGroups, err := group_m.GroupTopGetAll(model, area) if err != nil { return nil, err } result := make([]string, 0) for _, i := range topGroups { // 过滤掉被封禁的群。理论上是不需要的,因为被封禁的不会被置顶。这里只是为了保险 if bannedGroups[i] != 0 { continue } // 过滤掉被隐藏的群 if _, exist := hiddenGroups[i]; exist { continue } result = append(result, i) } return result, nil } type GetLatestGroupsReq struct { PageSize int `form:"pageSize"` // binding:"min=1" PageIndex int `form:"pageIndex"` LastId int `form:"lastId"` } // @Tags 群组 // @Summary 查询最新群组 // @Accept application/x-www-form-urlencoded // @Param token header string true "token" // @Param nonce header string true "随机数字" // @Param pageSize query int false "分页大小 默认:10" default(10) // @Param pageIndex query int false "第几个分页,从1开始 默认:1" default(1) // @Param lastId query int false "上一页列表的最后一个id,避免分页重复 默认:0" default(1) // @Success 200 {object} group_cv.LatestGroupInfo // @Router /v1/imGroup/latest [get] func GetLatestGroups(c *gin.Context) (*mycontext.MyContext, error) { myContext := mycontext.CreateMyContext(c.Keys) request := &GetLatestGroupsReq{} err := c.ShouldBindQuery(request) if err != nil { return myContext, err } if request.PageSize <= 0 { request.PageSize = 10 } if request.PageIndex <= 0 { request.PageIndex = 1 } // 上一页最后一个id, 避免分页变化数据重复 if request.LastId <= 0 { request.LastId = 0 } //offset := (req.PageIndex - 1) * req.PageSize model := domain.CreateModelContext(myContext) micGroupNum, err := group_m.GetMicHasInGroupNum(model) if err != nil { return myContext, err } gameGroups := group_m.GetGameGroupsMap(model) gids := make([]string, 0) for i, _ := range micGroupNum { // 过滤游戏房 if gameGroups[i] { continue } gids = append(gids, i) } // 获取最新群组列表 groupInfos, err := group_m.GetLatestGroupInfos(model, request.PageSize, request.LastId, gids) if err != nil { return myContext, err } // 提取id groupIds := make([]string, 0, len(groupInfos)) for _, group := range groupInfos { groupIds = append(groupIds, group.ImGroupId) } // 获取国家信息 countryInfo, err := res_c.GetCountryIconMap(model) if err != nil { return myContext, err } // 获取本周最高的扶持等级 supportLevels, err := group_s.NewGroupService(myContext).GetWeekMaxSupportLevelMap() if err != nil { return myContext, err } // 用户id myUserId, err := req.GetUserId(c) if err != nil { return myContext, err } // 获取房间浏览数量 visitCount, err := group_m.BatchGetRoomVisitCount(model.Log, groupIds) if err != nil { return myContext, err } // 获取群势力信息 owners := make([]uint64, 0) for _, group := range groupInfos { owners = append(owners, group.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 } // 获取房间人数信息 roomCount, err := group_m.BatchGetRoomCount(model, groupIds) if err != nil { return nil, err } // 获取火箭信息 rr := rocket_m.RocketResult{} maxStageMap, err := rr.GetMaxStage(mysql.Db, groupIds) if err != nil { return myContext, err } // 正在进行的游戏 games := game_m.GetNotEndGamesMap(model) result := make([]*group_cv.LatestGroupInfo, 0) for _, group := range groupInfos { micUsers := make([]user_cv.CvUserTiny, 0) // 密码应该是一直为空? var password *string = nil if len(group.Password) > 0 && group.Owner != myUserId { emptyStr := "" password = &emptyStr } // 勋章信息 medals := make([]medal_cv.PicElement, 0) if m, ok := groupMedals[group.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, group.ImGroupId) if err != nil { model.Log.Infof("GetLatestGroups: GetGroupConsumeMedal: %s", err.Error()) } else if pe != nil { medals = append(medals, medal_cv.PicElement{PicUrl: pe.PicUrl}) } // 最大的火箭 var maxStage *uint16 = nil if s, ok := maxStageMap[group.ImGroupId]; ok { maxStage = &s } result = append(result, &group_cv.LatestGroupInfo{ GroupInfo: group_cv.GroupInfo{ GroupBasicInfo: group_cv.GroupBasicInfo{ Id: group.Id, GroupId: group.TxGroupId, Name: group.Name, Introduction: group.Introduction, Notification: group.Notification, FaceUrl: group.FaceUrl, Code: group.Code, CountryIcon: countryInfo[group.Country], Password: password, SupportLevel: supportLevels[group.ImGroupId], GroupInUserDuration: visitCount[group.ImGroupId], MicNumType: int(group.MicNumType), GroupMedals: medals, }, HasOnMic: true, GroupPowerId: powerIds[group.Owner], GroupPowerName: powerNames[group.Owner], GroupPowerNameplate: powerNameplates[group.Owner], GroupPower: group_cv.GroupPower{ Id: powerIds[group.Owner], Name: powerNames[group.Owner], Nameplate: powerNameplates[group.Owner], Grade: powerGrades[group.Owner], }, }, MicUsers: micUsers, RoomUserCount: uint(roomCount[group.ImGroupId]), MaxStage: maxStage, GameTypes: games[group.TxGroupId], }) } resp.ResponseOk(c, result) return myContext, nil } // @Tags 群组 // @Summary 查询推荐的群组(version>=2.19) // @Accept application/x-www-form-urlencoded // @Param token header string true "token" // @Param nonce header string true "随机数字" // @Success 200 {object} group_cv.PopularGroupInfo // @Router /v1/imGroup/recommended [get] func GetRecommendGroup(c *gin.Context) (*mycontext.MyContext, error) { myContext := mycontext.CreateMyContext(c.Keys) model := domain.CreateModelContext(myContext) bannedGroups, err := group_m.GetBannedGroupsMap(model) if err != nil { return myContext, err } //noPwdGroups, err := group_m.FindNoPasswordGroups(model) //if err != nil { // return myContext, err //} // 获取麦上有人的所有群组及麦上人数 micGroupNum, err := group_m.GetMicHasInGroupNum(model) if err != nil { return myContext, err } var micGroupIds []string for groupId := range micGroupNum { micGroupIds = append(micGroupIds, groupId) } model.Log.Infof("GetRecommendGroups, micGroupNum : %v", micGroupNum) noPwdGroups, err := group_m.FindNoPasswordGroupsV2(model, micGroupIds) if err != nil { return myContext, err } model.Log.Infof("GetRecommendGroups: noPwdGroups = %d, bannedGroups = %d", len(noPwdGroups), len(bannedGroups)) groupIds := make([]string, 0) banCount := 0 for _, v := range noPwdGroups { if _, ok := micGroupNum[v.ImGroupId]; ok { if bannedGroups[v.ImGroupId] != 0 { banCount++ } else { groupIds = append(groupIds, v.ImGroupId) } } } model.Log.Infof("GetRecommendGroups: size = %d, banned %d", len(groupIds), banCount) myService := domain.CreateService(myContext) result, _, err := group_cv.BuildJoinedGroupInfo(myService, 0, groupIds, group_e.GROUP_RECOMMEND_SIZE, 1) if err != nil { return myContext, err } resp.ResponseOk(c, result) return myContext, nil } // @Tags 群组 // @Summary 获取最近访问的房间 // @Accept application/x-www-form-urlencoded // @Param token header string true "token" // @Param nonce header string true "随机数字" // @Success 200 {object} group_cv.JoinedGroupInfo // @Router /v1/imGroup/myRecent [get] func GetRecentGroup(c *gin.Context) (*mycontext.MyContext, error) { myContext := mycontext.CreateMyContext(c.Keys) userId, _, err := req.GetUserIdAndExtId(c, myContext) if err != nil { return myContext, err } model := domain.CreateModelContext(myContext) //uer := group_m.UserEnterRoom{UserId: userId} //rec, err := uer.Find(model.Db) //if err != nil { // return myContext, err //} rec := group_c.GetUserRecentRooms(model, userId) myGroups, err := group_m.FindGroupMapByOwner(model, userId) if err != nil { return myContext, err } // 剔除自己创建的群 groupIds := make([]string, 0) for _, groupId := range rec { if _, ok := myGroups[groupId]; !ok { groupIds = append(groupIds, groupId) } } myService := domain.CreateService(myContext) result, _, err := group_cv.BuildJoinedGroupInfo(myService, userId, groupIds, 30, 1) if err != nil { return myContext, err } resp.ResponseOk(c, result) return myContext, nil } // @Tags 群组 // @Summary 获取我成为永久会员的群 // @Accept application/x-www-form-urlencoded // @Param token header string true "token" // @Param nonce header string true "随机数字" // @Param pageSize query int false "分页大小 默认:10" default(10) // @Param pageIndex query int false "第几个分页,从1开始 默认:1" default(1) // @Success 200 {object} group_cv.JoinedGroupInfo // @Router /v1/imGroup/myPermanent [get] func GetMyGroup(c *gin.Context) (*mycontext.MyContext, error) { myContext := mycontext.CreateMyContext(c.Keys) 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 } userId, _, err := req.GetUserIdAndExtId(c, myContext) if err != nil { return myContext, err } model := domain.CreateModelContext(myContext) rec, err := group_m.GetJoinedGroups(model.Db, userId) if err != nil { return myContext, err } myGroups, err := group_m.FindGroupMapByOwner(model, userId) if err != nil { return myContext, err } // 剔除自己创建的群 groupIds := make([]string, 0) for _, i := range rec { if _, ok := myGroups[i.GroupId]; !ok { groupIds = append(groupIds, i.GroupId) } } myService := domain.CreateService(myContext) result, _, err := group_cv.BuildJoinedGroupInfo(myService, userId, groupIds, pageSize, pageIndex) if err != nil { return myContext, err } resp.ResponseOk(c, result) return myContext, nil } // @Tags 群组 // @Summary 最近访问列表 // @Accept application/x-www-form-urlencoded // @Param token header string true "token" // @Param nonce header string true "随机数字" // @Param groupId path string true "群ID" // @Param pageSize query int false "分页大小 默认:10" default(10) // @Param pageIndex query int false "第几个分页,从1开始 默认:1" default(1) // @Success 200 {object} GetGroupVisitorsRsp // @Router /v1/imGroup/visitors/{groupId} [get] func GetGroupVisitors(c *gin.Context) (*mycontext.MyContext, error) { myContext := mycontext.CreateMyContext(c.Keys) groupId := c.Param("groupId") if len(groupId) <= 0 { return myContext, bizerr.ParaMissing } 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 } userId, _, err := req.GetUserIdAndExtId(c, myContext) if err != nil { return myContext, err } model := domain.CreateModelContext(myContext) groupId, err = group_m.ToImGroupId(model, groupId) if err != nil { return myContext, err } //uer := group_m.UserEnterRoom{GroupId: groupId} //rows, err := uer.Find(model.Db) //if err != nil { // return myContext, err //} rows := group_c.GetLastRoomVisitors(model, groupId) //now := time.Now() //t := now.AddDate(0, 0, -15) // 只要15天内进入房间的人 userIds := make([]uint64, 0) for _, u := range rows { //if i.EnterTime.After(t) { userIds = append(userIds, u) //} } users, err := user_m.GetUserMapByIds(model, userIds) if err != nil { return myContext, err } model.Log.Infof("GetGroupVisitors %s: memberNum = %d, user size = %d", groupId, len(rows), len(userIds)) result := GetGroupVisitorsRsp{Total: uint(len(userIds))} beginPos := pageSize * (pageIndex - 1) if uint(beginPos) < result.Total { // 取在线状态 extIds := make([]string, 0) for _, i := range users { extIds = append(extIds, i.ExternalId) } statusMap, err := tim_m.GetOnlineStatus(model, extIds) if err != nil { return myContext, err } result.Online = 0 for _, v := range statusMap { if v == online_e.IM_STATUS_ON_LINE { result.Online++ } } model.Log.Infof("GetGroupVisitors %s: statusMap size = %d, onLine = %d", groupId, len(statusMap), result.Online) roles, _, err := group_m.GetRolesInGroup(model, groupId) if err != nil { return myContext, err } nobleLevels, err := noble_m.BatchGetNobleLevel(model.Db, userIds) if err != nil { return myContext, err } svipLevels, err := rpc.MGetUserSvipLevel(model, userIds) if err != nil { return myContext, err } vips, err := user_m.BatchGetVips(userIds) if err != nil { return myContext, err } model.Log.Infof("GetGroupVisitors %s, users %v, roles: %v, nobles: %v, vips: %v", groupId, userIds, roles, nobleLevels, vips) roomUsers, err := group_m.RoomLivingExistsUserId(groupId) if err != nil { return myContext, err } roomUserMap := utils.SliceToMapUInt64(roomUsers) //model.Log.Infof("GetGroupMembers %s, roomStatusMap %v", groupId, roomStatusMap) // 排序规则 :在房间的优先,其次是在线,再次看角色,最后看贵族 sort.Slice(userIds, func(i, j int) bool { ui := userIds[i] uj := userIds[j] _, ok1 := roomUserMap[ui] _, ok2 := roomUserMap[uj] if ok1 && !ok2 { return true } else if ok1 == ok2 { ei := users[ui].ExternalId ej := users[uj].ExternalId if statusMap[ei] > statusMap[ej] { return true } if statusMap[ei] == statusMap[ej] { if roles[ui] > roles[uj] { return true } if roles[ui] == roles[uj] { // SVIP>贵族5>贵族4>贵族3>贵族2>VIP if svipLevels[ui] > svipLevels[uj] { return true } else if svipLevels[ui] == svipLevels[uj] { if nobleLevels[ui] > nobleLevels[uj] && nobleLevels[ui] >= 2 { return true } if nobleLevels[ui] == nobleLevels[uj] || nobleLevels[ui] < 2 && nobleLevels[uj] < 2 { if vips[ui] != nil { if vips[uj] == nil { return true } else { return users[ui].Code < users[uj].Code } } else if vips[uj] == nil { return users[ui].Code < users[uj].Code } } } } } } return false }) model.Log.Infof("GetGroupVisitors %s, sorted users: %v", groupId, userIds) endPos := pageSize * pageIndex if endPos > len(userIds) { endPos = len(userIds) } userIds = userIds[beginPos:endPos] userExtends, err := user_cv.BatchGetUserExtend(model, userIds, userId) if err != nil { return myContext, err } for _, u := range userIds { inRoom := false if _, ok := roomUserMap[u]; ok { inRoom = true } result.Members = append(result.Members, group_cv.GroupVisitorsDetail{ CvGroupMember: user_cv.CvUserExtendToCvGroupMember(userExtends[u]), Role: roles[u], OnlineStatus: statusMap[users[u].ExternalId], InRoom: inRoom, }) } } resp.ResponseOk(c, result) // 清理15天之前的访客 group_c.RemoveRoomVisitors(model, groupId) return myContext, nil } type OwnPublicGroupRsp struct { Total uint `json:"total"` MyGroups []group_cv.GroupDetail `json:"myGroups"` } // @Tags 群组 // @Summary 查询某人创建的(公开)群组 // @Accept application/x-www-form-urlencoded // @Param token header string true "token" // @Param nonce header string true "随机数字" // @Param userExternalId path string true "用户ExternalID" // @Success 200 {object} OwnPublicGroupRsp // @Router /v1/imGroup/ownPublicGroup/{userExternalId} [get] func GetOwnPublicGroup(c *gin.Context) (*mycontext.MyContext, error) { myContext := mycontext.CreateMyContext(c.Keys) externalId := c.Param("userExternalId") if len(externalId) <= 0 { return myContext, myerr.NewSysError("userExternalId 为必填项") } model := domain.CreateModelContext(myContext) user, err := user_m.GetUserByExtId(model, externalId) if err != nil { return myContext, err } if utils.IfLogout(user.LogoutTime) { resp.ResponseOk(c, OwnPublicGroupRsp{Total: 0}) return myContext, nil } theirRoles, err := group_m.GetRolesByUser(model, user.ID) if err != nil { return myContext, err } model.Log.Info("GetOwnPublicGroup: theirRoles - ", theirRoles) countryInfo, err := res_c.GetCountryIconMap(model) if err != nil { return myContext, err } permanentGroups, err := group_m.GetJoinedGroups(model.Db, user.ID) if err != nil { return myContext, err } groups, err := group_m.FindGroupMapByOwner(model, user.ID) if err != nil { return myContext, err } result := OwnPublicGroupRsp{Total: uint(len(permanentGroups) + len(groups))} groupId := "" for i, j := range groups { // 只要公开的群 if len(j.Password) == 0 { if r, ok := theirRoles[j.ImGroupId]; ok && r == group_e.GROUP_OWNER { // fixme:如果有多个群则展示群人数最多的群组 groupId = i break } } } if len(groupId) > 0 { groupInfo := groups[groupId] roleMembers, myRole, err := buildRoleMembers(model, groupId, user.ID) if err != nil { return myContext, err } // 截取前N个 endPos := group_e.GROUP_ROLE_PERSONAL_VIEW_LIMIT if endPos > len(roleMembers) { endPos = len(roleMembers) } // 找不到的,默认为空(可能被后台删了) themeUrl := "" var themeId uint64 = 0 var themeType uint8 = 1 if groupInfo.ThemeId > 0 { //官方主题 // 找不到的,默认为空(可能被后台删了) themeType = 1 themes, _ := res_m.GroupThemeGetAll(model.Db) for _, i := range themes { if i.ID == uint64(groupInfo.ThemeId) { themeUrl = i.Url themeId = i.ID break } } } else { //可能是自定义主题 themeId, themeUrl, err = group_m.GetShowCustomTheme(model, groupInfo.ImGroupId) if err != nil { return myContext, err } if themeId != 0 { themeType = 2 } } supportLevels, err := group_s.NewGroupService(myContext).GetWeekMaxSupportLevelMap() if err != nil { return myContext, err } mem, err := group_m.GetMembers(model.Db, groupId) if err != nil { return myContext, err } result.MyGroups = append(result.MyGroups, group_cv.GroupDetail{ GroupBasicInfo: group_cv.GroupBasicInfo{ GroupId: groupInfo.TxGroupId, Name: groupInfo.Name, Introduction: groupInfo.Introduction, Notification: groupInfo.Notification, FaceUrl: groupInfo.FaceUrl, Code: groupInfo.Code, MemberNum: uint(len(mem)), CountryIcon: countryInfo[groupInfo.Country], SupportLevel: supportLevels[groupId], MicNumType: int(groupInfo.MicNumType), }, MicOn: groupInfo.MicOn, LoadHistory: groupInfo.LoadHistory, ThemeId: themeId, ThemeUrl: themeUrl, ThemeType: themeType, Role: myRole, RoleMembers: roleMembers[0:endPos], }) } resp.ResponseOk(c, result) return myContext, nil } func buildRoleMembers(model *domain.Model, groupId string, userId uint64) ([]group_cv.RoleMemberInfo, group_e.GroupRoleType, error) { myRole := group_e.GROUP_VISITOR roles, _, err := group_m.GetRolesInGroup(model, groupId) if err != nil { return nil, myRole, err } model.Log.Infof("buildRoleMembers: roles - %s, %v", groupId, roles) userIds := make([]uint64, 0) for u, r := range roles { userIds = append(userIds, u) if u == userId { myRole = r } } users, err := user_cv.GetUserTinyMap(userIds) if err != nil { return nil, myRole, err } vips, err := user_m.BatchGetVips(userIds) if err != nil { return nil, myRole, err } extIds := make([]string, 0) for _, i := range users { extIds = append(extIds, i.ExternalId) } status, err := tim_m.GetOnlineStatus(model, extIds) if err != nil { return nil, myRole, err } superManagerMap, err := user_m.GetSuperManagerMap(userIds) if err != nil { return nil, myRole, err } roleMembers := make([]group_cv.RoleMemberInfo, 0) for u, _ := range users { m := users[u] roleMembers = append(roleMembers, group_cv.RoleMemberInfo{ CvUserBase: user_cv.CvUserBase{ Avatar: &m.Avatar, ExternalId: &m.ExternalId, Nick: &m.Nick, Sex: &m.Sex, Country: &m.CountryIcon, CountryIcon: &m.CountryIcon, Code: &m.Code, IsPrettyCode: m.IsPrettyCode, IsVip: vips[m.Id] != nil, IsOfficialStaff: superManagerMap[m.Id], VipExpireTime: vips[m.Id], }, Role: roles[u], OnlineStatus: status[m.ExternalId], }) } sort.SliceStable(roleMembers, func(i, j int) bool { if roleMembers[i].Role > roleMembers[j].Role { return true } else if roleMembers[i].Role == roleMembers[j].Role { if roleMembers[i].OnlineStatus > roleMembers[j].OnlineStatus { return true } else if roleMembers[i].OnlineStatus == roleMembers[j].OnlineStatus && *roleMembers[i].Code > *roleMembers[j].Code { return true } } return false }) return roleMembers, myRole, nil } // @Tags 群组 // @Summary 国家A,群主是属于国家A下的群 // @Param token header string true "token" // @Param nonce header string true "随机数字" // @Param countryShortName query string true "国家缩写" // @Param pageSize query int true "分页大小 默认:10" default(10) // @Param pageIndex query int true "第几个分页,从1开始 默认:1" default(1) // @Success 200 {object} group_cv.PopularGroupInfo // @Router /v1/imGroup/country [get] func GetGroupByCountry(c *gin.Context) (*mycontext.MyContext, error) { myContext := mycontext.CreateMyContext(c.Keys) myUserId, err := req.GetUserId(c) if err != nil { return myContext, err } 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 } model := domain.CreateModelContext(myContext) bannedGroups, err := group_m.GetBannedGroupsMap(model) if err != nil { return myContext, err } 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) if err != nil { return myContext, err } endTime := time.Now() model.Log.Infof("GetGroupByCountry: candidates size = %d, takes %d ms banned = %d, visitCount size = %d", len(groups), endTime.Sub(beginTime).Milliseconds(), banCount, len(visitCount)) model.Log.Infof("GetGroupByCountry cost1:%v", time.Now().Sub(beginTime)) hotGroupList := make([]*group_m.GroupInfo, 0) // 获取麦上有人的所有群组及麦上人数 micGroupNum, err := group_m.GetMicHasInGroupNum(model) if err != nil { return myContext, err } model.Log.Infof("GetGroupByCountry, micGroupNum : %v", micGroupNum) model.Log.Infof("GetGroupByCountry 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 myContext, err } model.Log.Infof("GetGroupByCountry, diamonds in 30 mins: %v", diamonds) model.Log.Infof("GetGroupByCountry cost3:%v", time.Now().Sub(beginTime)) supportLevels, err := group_s.NewGroupService(myContext).GetWeekMaxSupportLevelMap() if err != nil { return myContext, err } model.Log.Infof("GetGroupByCountry, supportLevels : %v", supportLevels) model.Log.Infof("GetGroupByCountry 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("GetGroupByCountry cost5:%v", time.Now().Sub(beginTime)) // for pretty log //logstr := "" // * 同语言区 ^ 麦上有人 + 开放群 - 需要等级的群 for _, i := range sortedGroupIds { hotGroupList = append(hotGroupList, groups[i]) //prefix := " " //if micGroupNum[i] > 0 { //prefix += "^" //} //logstr += prefix + i + ":" + groups[i].Code + ":" + // strconv.Itoa(int(supportLevels[i])) + ":" + strconv.Itoa(int(micGroupNum[i])) + ":" + strconv.Itoa(int(visitCount[i])) } total := len(hotGroupList) model.Log.Infof("GetGroupByCountry: size = %d, %s", total) result := make([]group_cv.PopularGroupInfo, 0) beginPos := pageSize * (pageIndex - 1) endPos := pageSize * pageIndex if beginPos < total { if endPos > total { endPos = total } groupIds := make([]string, 0) owners := make([]uint64, 0) for _, i := range hotGroupList[beginPos:endPos] { 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 } model.Log.Infof("GetGroupByCountry: final start = %d, end = %d, groupIds %v", beginPos, endPos, groupIds) countryInfo, err := res_c.GetCountryIconMap(model) if err != nil { return myContext, err } rr := rocket_m.RocketResult{} maxStageMap, err := rr.GetMaxStage(mysql.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) for _, i := range hotGroupList[beginPos:endPos] { 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], }) } } model.Log.Infof("GetGroupByCountry cost6:%v", time.Now().Sub(beginTime)) resp.ResponsePageOk(c, result, uint(total), pageIndex) 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) if err != nil { return nil, 0, nil, err } roomVisitCount, err := room_c.GetAllRoomVisitCount() if err != nil { return nil, 0, nil, err } 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 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 } // @Tags 资源 // @Summary 国家资源 // @Param token header string true "token" // @Param timestamp header string true "时间戳" // @Param nonce header string true "随机数字" // @Param signature header string true "sha1加密结果" // @Param deviceType header string true "系统类型 ios android" // @Param deviceVersion header string true "系统版本" // @Success 200 {object} group_cv.CvCountry // @Router /v1/imGroup/country/prior [get] func GroupountryPrior(c *gin.Context) (*mycontext.MyContext, error) { myContext := mycontext.CreateMyContext(c.Keys) //沙特、科威特、伊拉克、阿尔及利亚、印度、菲律宾、土耳其 //沙特,伊拉克,科威特,土耳其。印度,巴基斯坦,印尼 resCountry, err := res_m.GetAllCountryByFilter(domain.CreateModelNil(), []string{"KSA", "Iraq", "Kuwait", "Turkey", "India", "Pakistan", "Indonesia"}) if err != nil { return myContext, err } var cvContrys []group_cv.CvCountry for i := 0; i < len(resCountry); i++ { cvContrys = append(cvContrys, group_cv.CvCountry{ Name: &resCountry[i].Name, ShortName: &resCountry[i].ShortName, Icon: &resCountry[i].Icon, Code: &resCountry[i].Code, }) } resp.ResponseOk(c, cvContrys) return myContext, nil }