go-common/app/interface/live/app-interface/service/v2/livehomepage.go
2019-04-22 18:49:16 +08:00

579 lines
18 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package v2
import (
"context"
"math"
"strconv"
"go-common/app/service/live/third_api/bvc"
"go-common/library/sync/errgroup"
"github.com/pkg/errors"
v2pb "go-common/app/interface/live/app-interface/api/http/v2"
"go-common/app/interface/live/app-interface/conf"
"go-common/app/interface/live/app-interface/dao"
relationV1 "go-common/app/interface/live/app-interface/service/v1"
relationT "go-common/app/interface/live/app-interface/service/v1/relation"
avV1 "go-common/app/service/live/av/api/liverpc/v1"
relationRpcV1 "go-common/app/service/live/relation/api/liverpc/v1"
roomV1 "go-common/app/service/live/room/api/liverpc/v1"
accountM "go-common/app/service/main/account/model"
actmdl "go-common/app/service/main/account/model"
account "go-common/app/service/main/account/rpc/client"
"go-common/library/ecode"
"go-common/library/log"
)
// RelationService struct
type RelationService struct {
conf *conf.Config
accountRPC *account.Service3
}
const (
relationPageSize = 4
app536relationPageSize = 2
)
// CheckLiveAnchorParams ... implementation
// [app端关注首页]入参校验
func CheckLiveAnchorParams(ctx context.Context, page int64) (uid int64, relationPage int64, err error) {
mid := relationT.GetUIDFromHeader(ctx)
relationPage = page
err = nil
if mid == 0 {
err = errors.WithMessage(ecode.NeedLogIn, "GET SEA PATROL FAIL")
return
}
if page <= 0 {
err = errors.WithMessage(ecode.ResourceParamErr, "GET SEA PATROL FAIL")
return
}
return mid, relationPage, err
}
// LiveAnchorHomePage ... implementation
// [app端关注首页]正在直播接口
func (s *IndexService) LiveAnchorHomePage(ctx context.Context, relationPage int64, build int64, platform string, quality int64) (Resp []*v2pb.MMyIdol) {
List := make([]*v2pb.MyIdolItem, 0)
ExtraInfo := &v2pb.MyIdolExtra{CardType: relationT.App533CardType}
Resp = make([]*v2pb.MMyIdol, 0)
s.MakeLiveAnchorDefaultResult(Resp, ExtraInfo)
uid, relationPage, err := CheckLiveAnchorParams(ctx, relationPage)
if err != nil && uid != 0 {
log.Error("[LiveAnchorHomePage]CheckParamsError,uid:%d,relationPage:%d", uid, relationPage)
return
}
wg, _ := errgroup.WithContext(ctx)
relationInfo, groupList, mapUfos2Rolaids, mapRoomID2UID, AllRoomID, err := relationV1.GetAttentionListAndGroup(ctx)
if err != nil {
log.Error("[LiveAnchorHomePage]get_attentionList_rpc_error")
return
}
// 获取全量room信息,不过滤
roomParams := &roomV1.RoomGetStatusInfoByUidsReq{Uids: groupList["all"], FilterOffline: 0, NeedBroadcastType: 1}
// room
roomResp := make(map[int64]*roomV1.RoomGetStatusInfoByUidsResp_RoomInfo)
userResp := make(map[int64]*accountM.Card)
roomCornerResp := make(map[int64]*roomV1.RoomPendantGetPendantByIdsResp_Result)
pkResp := make(map[string]int64)
wg.Go(func() error {
roomResp, err = s.GetRoomInfo(ctx, roomParams)
return err
})
if err = wg.Wait(); nil != err {
log.Error("[LiveAnchorHomePage][first_step]get_room_rpc_error")
return
}
livingUfos := make([]int64, 0)
livingRolaids := make([]int64, 0)
livingRoomInfo := GetLivingRooms(roomResp)
// 没有人直播
if len(livingRoomInfo) == 0 {
GetLastLiveAnchorInfo(ctx, roomResp, AllRoomID, mapRoomID2UID, ExtraInfo)
moduleInfoMap := s.GetAllModuleInfoMapFromCache(ctx)
for _, m := range moduleInfoMap[_feedType] {
Resp = append(Resp, &v2pb.MMyIdol{ModuleInfo: m, List: List, ExtraInfo: ExtraInfo})
}
return
}
wgHasLive := &errgroup.Group{}
wgHasLive, _ = errgroup.WithContext(ctx)
for k, v := range livingRoomInfo {
livingUfos = append(livingUfos, k)
livingRolaids = append(livingRolaids, v.RoomId)
}
// user信息
wgHasLive.Go(func() error {
userResp, err = s.GetUserInfo(ctx, livingUfos)
return err
})
// room
roomPendentParams := &roomV1.RoomPendantGetPendantByIdsReq{Ids: livingRolaids, Type: relationT.PendentMobileBadge, Position: relationT.PendentPosition}
wgHasLive.Go(func() error {
roomCornerResp, err = s.GetRoomPendantInfo(ctx, roomPendentParams)
return err
})
// pk_id
pkParams := &avV1.PkGetPkIdsByRoomIdsReq{RoomIds: livingRolaids, Platform: platform}
wgHasLive.Go(func() error {
pkResp, err = s.GetPkID(ctx, pkParams)
return err
})
if err = wgHasLive.Wait(); nil != err {
log.Error("[LiveAnchorHomePage][second_step]room/main.account/pkID/rpc_error")
return
}
attentionRoomListPlayURLMap := dao.BvcApi.GetPlayUrlMulti(ctx, livingRolaids, 0, quality, build, platform)
// 下游数据收集完成
mapSp := make([]int64, 0)
normalSp := make([]int64, 0)
mapSp = append(mapSp, groupList["special"]...)
normalSp = append(normalSp, groupList["normal"]...)
List = AdaptLivingField(livingRoomInfo, roomCornerResp, userResp, relationInfo, pkResp, attentionRoomListPlayURLMap, mapSp, normalSp, mapUfos2Rolaids)
ExtraInfo.TotalCount = int64(len(List))
// 注释原因app536灰度策略,需要恢复2卡样式(之前在535已全量4卡,但是需求变了)产品:古月
// https://www.tapd.cn/20082211/prong/stories/view/1120082211001104459
// userExtParams := &userExV1.GrayRuleGetByMarkReq{Mark: relationT.App536GrayRule}
// grayRule, err := relationT.GetGrayRule(ctx, userExtParams)
// var UserExApp536Rule string
// if err != nil {
// log.Error("[LiveAnchorHomePage]get_GrayRule_rpc_error")
// UserExApp536Rule = ""
// } else if grayRule != nil {
// UserExApp536Rule = grayRule.Content
// }
SliceList, page := s.SliceForHomePage(List, relationPage, uid, platform)
ExtraInfo.RelationPage = page
var result v2pb.MMyIdol
result.ExtraInfo = ExtraInfo
result.List = SliceList
moduleInfoMap := s.GetAllModuleInfoMapFromCache(ctx)
for _, m := range moduleInfoMap[_feedType] {
Resp = append(Resp, &v2pb.MMyIdol{ModuleInfo: m, List: result.List, ExtraInfo: result.ExtraInfo})
}
return
}
// AdaptLivingField ... implementation
// [app端关注首页]填充数据
func AdaptLivingField(roomInfo map[int64]*roomV1.RoomGetStatusInfoByUidsResp_RoomInfo,
roomPendentInfo map[int64]*roomV1.RoomPendantGetPendantByIdsResp_Result,
userResult map[int64]*accountM.Card,
relationInfo map[int64]*relationRpcV1.BaseInfoGetFollowTypeResp_UidInfo,
pkIDInfo map[string]int64, playURLInfo map[int64]*bvc.PlayUrlItem, specialUID []int64, normalUID []int64,
mapUfos2Rolaids map[int64]int64) (resp []*v2pb.MyIdolItem) {
resp = make([]*v2pb.MyIdolItem, 0)
normalResp := make([]*v2pb.MyIdolItem, 0)
resp = make([]*v2pb.MyIdolItem, 0)
if len(specialUID) > 0 {
item := LiveFireField(roomInfo, roomPendentInfo, userResult, pkIDInfo, playURLInfo, relationInfo, specialUID, mapUfos2Rolaids)
resp = AppSortRuleOnline(item)
}
if len(normalUID) > 0 {
tempResp := LiveFireField(roomInfo, roomPendentInfo, userResult, pkIDInfo, playURLInfo, relationInfo, normalUID, mapUfos2Rolaids)
normalResp = AppSortRuleOnline(tempResp)
}
if len(normalResp) > 0 {
resp = append(resp, normalResp...)
}
return
}
// SliceForHomePage ... implementation
// app534规则 [app端关注首页]首页slice逻辑,客户端只显示偶数个数,为兼容推荐去重,当个数为3时返回2
// https://www.tapd.cn/20082211/prong/stories/view/1120082211001067961
// https://www.tapd.cn/20082211/prong/stories/view/1120082211001085685
//
// app536规则 https://www.tapd.cn/20082211/prong/stories/view/1120082211001104459
func (s *IndexService) SliceForHomePage(input []*v2pb.MyIdolItem, page int64, uid int64, platform string) (resp []*v2pb.MyIdolItem, relationPage int64) {
resp = make([]*v2pb.MyIdolItem, 0)
grayRule := s.App536ABTest(uid, platform)
relationPage = page
if len(input) <= 0 {
return
}
count := int64(len(input))
// 536规则
if grayRule == 1 {
switch count {
case 1:
{
resp = input[:]
relationPage = 1
return
}
case 2:
{
resp = input[:]
relationPage = 1
return
}
}
var pageSize int64
pageSize = page
if page < 1 {
pageSize = 1
}
start := (pageSize - 1) * app536relationPageSize
end := int64(start + app536relationPageSize)
// 正常slice
if end <= count {
resp = input[start:end]
} else {
// 回环逻辑,最后一页不足pagesize时返回第一页
relationPage = 1
startIndex := 0
var startCount int64
if count > app536relationPageSize {
startCount = app536relationPageSize
} else {
startCount = count
}
resp = input[startIndex:startCount]
}
return
}
// 536之前4卡逻辑
switch count {
case 1:
{
resp = input[:]
relationPage = 1
return
}
case 2:
{
resp = input[:]
relationPage = 1
return
}
case 3:
{
resp = input[0:2]
relationPage = 1
return
}
}
var pageSize int64
pageSize = page
if page < 1 {
pageSize = 1
}
start := (pageSize - 1) * relationPageSize
end := int64(start + relationPageSize)
if end <= count {
resp = input[start:end]
} else {
relationPage = 1
startIndex := 0
var startCount int64
if count > relationPageSize {
startCount = relationPageSize
} else {
startCount = count
}
resp = input[startIndex:startCount]
}
return
}
// LiveFireField ... implementation
// [app端关注首页]填充数据
func LiveFireField(roomInfo map[int64]*roomV1.RoomGetStatusInfoByUidsResp_RoomInfo,
roomPendentInfo map[int64]*roomV1.RoomPendantGetPendantByIdsResp_Result,
userResult map[int64]*accountM.Card,
pkIDInfo map[string]int64, playURLInfo map[int64]*bvc.PlayUrlItem,
relationInfo map[int64]*relationRpcV1.BaseInfoGetFollowTypeResp_UidInfo,
ufos []int64, mapUfos2Rolaids map[int64]int64) (resp []*v2pb.MyIdolItem) {
for _, v := range ufos {
item := v2pb.MyIdolItem{}
roomID, roomIDExist := mapUfos2Rolaids[v]
if !roomIDExist {
continue
}
roomItem := roomInfo[v]
roomPendentItem := roomPendentInfo[roomID]
userItem := userResult[v]
relationItem := relationInfo[v]
pkItem := pkIDInfo[strconv.Itoa(int(roomID))]
playURLItem := playURLInfo[roomID]
if roomItem == nil || userItem == nil || relationItem == nil {
continue
}
PlayURL := ""
PlayURL265 := ""
PlayURLAcc := make([]int64, 0)
PlayURLCur := 0
PendentRu := ""
PendentRuColor := ""
PendentRuPic := ""
if playURLItem != nil {
PlayURL = playURLItem.Url["h264"]
PlayURL265 = playURLItem.Url["h265"]
PlayURLAcc = playURLItem.AcceptQuality
PlayURLCur = int(playURLItem.CurrentQuality)
}
if roomPendentItem != nil {
PendentRu = roomPendentItem.Value
PendentRuColor = roomPendentItem.BgColor
PendentRuPic = roomPendentItem.BgPic
}
item.Roomid = roomItem.RoomId
item.Uid = roomItem.Uid
item.Uname = userItem.Name
item.Face = userItem.Face
item.Title = roomItem.Title
item.LiveTagName = roomItem.AreaV2Name
item.LiveTime = roomItem.LiveTime
item.Online = roomItem.Online
item.PlayUrl = PlayURL
item.PlayUrlH265 = PlayURL265
item.AcceptQuality = PlayURLAcc
item.CurrentQuality = int64(PlayURLCur)
item.PkId = pkItem
item.Area = roomItem.Area
item.AreaName = roomItem.AreaName
item.AreaV2Id = roomItem.AreaV2Id
item.AreaV2Name = roomItem.AreaV2Name
item.AreaV2ParentId = roomItem.AreaV2ParentId
item.AreaV2ParentName = roomItem.AreaV2ParentName
item.BroadcastType = roomItem.BroadcastType
item.Link = relationT.LiveDomain + strconv.Itoa(int(roomID)) + relationT.BoastURL + strconv.Itoa(int(item.BroadcastType))
item.OfficialVerify = int64(RoleMap(userItem.Official.Role))
item.SpecialAttention = relationItem.Special
item.PendentRu = PendentRu
item.PendentRuColor = PendentRuColor
item.PendentRuPic = PendentRuPic
if len(roomItem.CoverFromUser) == 0 {
item.Cover = roomItem.Keyframe
} else {
item.Cover = roomItem.CoverFromUser
}
resp = append(resp, &item)
}
return
}
// MakeLiveAnchorDefaultResult ...
// 正在直播默认返回
func (s *IndexService) MakeLiveAnchorDefaultResult(Resp []*v2pb.MMyIdol, ExtraInfo *v2pb.MyIdolExtra) {
if ExtraInfo != nil {
ExtraInfo.TotalCount = 0
ExtraInfo.TagsDesc = ""
ExtraInfo.UnameDesc = ""
ExtraInfo.TimeDesc = ""
// [历史原因]cardType只能为1,否则客户端报错,见 https://www.tapd.cn/20082211/prong/stories/view/1120082211001086997
ExtraInfo.CardType = 1
ExtraInfo.RelationPage = 1
}
moduleInfoMap := s.GetAllModuleInfoMapFromCache(context.TODO())
for _, m := range moduleInfoMap[_feedType] {
Resp = append(Resp, &v2pb.MMyIdol{ModuleInfo: m, List: []*v2pb.MyIdolItem{}, ExtraInfo: ExtraInfo})
}
}
// GetLivingRooms ... implementation
// [app端关注首页]获取正在直播房间
func GetLivingRooms(roomResult map[int64]*roomV1.RoomGetStatusInfoByUidsResp_RoomInfo) (liveRoom map[int64]*roomV1.RoomGetStatusInfoByUidsResp_RoomInfo) {
liveRoom = make(map[int64]*roomV1.RoomGetStatusInfoByUidsResp_RoomInfo)
if len(roomResult) == 0 {
return
}
for k, v := range roomResult {
if v.LiveStatus == relationV1.RoomStatusLive {
liveRoom[k] = v
}
}
return
}
// CheckLiveAnchorParams implementation
// 入参校验
func (s *IndexService) CheckLiveAnchorParams(ctx context.Context, req *v2pb.GetAllListReq) (uid int64, relationPage int64, err error) {
if req == nil {
err = ecode.LiveAnchorReqV2ParamsNil
return
}
uid = relationT.GetUIDFromHeader(ctx)
if uid == 0 {
err = errors.WithMessage(ecode.NeedLogIn, "GET SEA PATROL FAIL")
return
}
if req.RelationPage <= 0 {
log.Error("CallRelationLiveAnchorV2ParamsCheckError|relationPage:%d", req.RelationPage)
err = errors.WithMessage(ecode.LiveAnchorReqV2ParamsError, "GET SEA PATROL FAIL")
return
}
return
}
// GetLastLiveAnchorInfo ... implementation
// [app端关注首页]获取最新一次直播信息
func GetLastLiveAnchorInfo(ctx context.Context, roomResult map[int64]*roomV1.RoomGetStatusInfoByUidsResp_RoomInfo,
RoomIDs []int64, RoomID2UID map[int64]int64, ExtraInfo *v2pb.MyIdolExtra) (uid int64, relationPage int64, err error) {
if len(roomResult) == 0 || len(RoomIDs) == 0 || len(RoomID2UID) == 0 {
return
}
lastLiveTime, _ := relationT.GetLastLiveTime(ctx, RoomIDs)
_, _, sorted := relationV1.GetLastAnchorLiveTime(lastLiveTime)
var firstRoom int64
var firstValue int64
if sorted.Len() > 0 {
for _, v := range sorted {
firstRoom = int64(v.Key)
firstValue = int64(v.Value)
break
}
firstUID := int64(RoomID2UID[firstRoom])
tempTime := make(map[int64]int64)
if firstValue > 0 {
tempTime[firstUID] = firstValue
if roomItem, exist := roomResult[firstUID]; exist {
ExtraInfo.UnameDesc = roomItem.Uname
ExtraInfo.TagsDesc = roomItem.AreaV2Name
liveDesc, _ := relationV1.TimeLineRule(tempTime, nil)
ExtraInfo.TimeDesc = liveDesc[firstUID]
}
}
}
return
}
// GetUserInfoData ...
// 调用account grpc接口cards获取用户信息
func (s *RelationService) GetUserInfoData(ctx context.Context, UIDs []int64) (userResult map[int64]*accountM.Card, err error) {
rpcChunkSize, RPCTimeout, err := relationT.GetChunkInfo(relationT.AccountGRPC)
params := relationT.ChunkCallInfo{ParamsName: "ufos", URLName: relationT.AccountGRPC, ChunkSize: rpcChunkSize, RPCTimeout: RPCTimeout}
userResult = make(map[int64]*accountM.Card)
lens := len(UIDs)
if lens <= 0 {
return
}
// 批次
params.ChunkNum = int64(math.Ceil(float64(lens) / float64(params.ChunkSize)))
chunkResult := make([]map[int64]*accountM.Card, params.ChunkNum)
wg, _ := errgroup.WithContext(ctx)
for i := int64(1); i <= params.ChunkNum; i++ {
x := i
wg.Go(func() error {
chunkUfosIds := make([]int64, 20)
if x == params.ChunkNum {
chunkUfosIds = UIDs[(x-1)*params.ChunkSize:]
} else {
chunkUfosIds = UIDs[(x-1)*params.ChunkSize : x*params.ChunkSize]
}
ret, err := s.accountRPC.Cards3(ctx, &actmdl.ArgMids{Mids: chunkUfosIds})
if err != nil {
err = errors.WithMessage(ecode.AccountGRPCError, "GET SEA PATROL FAIL")
log.Error("Call main.Account.Cards Error.Infos(%+v) error(%+v)", chunkUfosIds, err)
}
chunkResult[x-1] = ret
return nil
})
}
if err := wg.Wait(); err != nil {
erelongInfo := relationT.ErrLogStrut{}
erelongInfo.ErrType = "GoRoutingWaitError"
erelongInfo.URLName = relationT.AccountGRPC
erelongInfo.ErrDesc = relationT.GoRoutingErr
erelongInfo.Code = 1003001
erelongInfo.RPCTimeout = params.RPCTimeout
erelongInfo.ErrorPtr = &err
log.Error(erelongInfo.ErrType+"|"+erelongInfo.URLName+"|error:%+v"+"|Code:%d"+"|Msg:%s"+"|RPCTimeout:%d"+"|ChunkSize:%d"+"|ChunkNum:%d"+"|ParamsName:%s",
*erelongInfo.ErrorPtr, erelongInfo.Code, erelongInfo.Msg, erelongInfo.RPCTimeout, erelongInfo.ChunkSize, erelongInfo.ChunkNum, params.ParamsName)
err = errors.WithMessage(ecode.AccountGRPCFrameError, "GET SEA PATROL FAIL")
return nil, err
}
// 整理数据
for _, chunkItemList := range chunkResult {
for _, item := range chunkItemList {
if item != nil {
userResult[item.Mid] = item
}
}
}
return
}
// App536ABTest ... hard code配置
// ABTest
func (s *IndexService) App536ABTest(mid int64, platform string) (grayType int64) {
// 因为ipad屏幕尺寸较大,展示2卡会影响体验,故如果是ipad客户端则使用4卡样式,产品tianyumo
if platform == "ipad" {
return 0
}
mUID := mid % 100
if mUID >= 0 && mUID <= 89 {
// 4卡
return 0
}
return 1
}
// 后台配置
// // App536ABTest ...
// // ABTest
// func (s *IndexService) App536ABTest(content string, mid int64) (grayType int64) {
// if len(content) == 0 {
// grayType = 0
// return
// }
// resultMap := make(map[string]int64)
// resultMap["app536_4card_type"] = 0
// resultMap["app536_2card_type"] = 1
// typeMap := make([]string, 0)
// mr := &[]GrayRule{}
// if err := json.Unmarshal([]byte(content), mr); err != nil {
// grayType = 0
// return
// }
// ruleArr := *mr
// scoreMap := make(map[string]int)
//
// for _, v := range ruleArr {
// scoreMap[v.Mark] = int(RParseInt(v.Value, 100))
// }
// sortedScore := SortMapByValue(scoreMap)
// scoreEnd := make([]int, 0)
// for _, v := range sortedScore {
// scoreEnd = append(scoreEnd, v.Value)
// typeMap = append(typeMap, v.Key)
// }
// score1 := scoreEnd[0]
// score2 := 100
// section1 := make(map[int]bool)
// section2 := make(map[int]bool)
// for section1Loop := 0; section1Loop < score1; section1Loop++ {
// section1[section1Loop] = true
// }
// for sectionLoop2 := score1; sectionLoop2 < score2; sectionLoop2++ {
// section2[sectionLoop2] = true
// }
// result := int(mid % 100)
// if scoreEnd[0] != 0 {
// if _, exist := section1[result]; exist {
// grayType = resultMap[typeMap[0]]
// return
// }
// }
// if scoreEnd[1] != 0 {
// if _, exist := section2[result]; exist {
// grayType = resultMap[typeMap[1]]
// return
// }
// }
// grayType = 0
// return
// }