123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532 |
- package v2
- import (
- "context"
- "sync/atomic"
- "time"
- "go-common/app/interface/live/app-interface/dao/account"
- "go-common/app/interface/live/app-interface/dao/av"
- "go-common/app/interface/live/app-interface/dao/fans_medal"
- "go-common/app/interface/live/app-interface/dao/live_data"
- "go-common/app/interface/live/app-interface/dao/rankdb"
- "go-common/app/interface/live/app-interface/dao/relation"
- "go-common/app/interface/live/app-interface/dao/room_ex"
- "go-common/app/interface/live/app-interface/dao/user_ext"
- 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"
- liveuserDao "go-common/app/interface/live/app-interface/dao/live_user"
- roomDao "go-common/app/interface/live/app-interface/dao/room"
- roomexDao "go-common/app/interface/live/app-interface/dao/room_ex"
- xuserDao "go-common/app/interface/live/app-interface/dao/xuser"
- liveUserV1 "go-common/app/service/live/live_user/api/liverpc/v1"
- recommendV1 "go-common/app/service/live/recommend/api/grpc/v1"
- roomV2 "go-common/app/service/live/room/api/liverpc/v2"
- xrf "go-common/app/service/live/xroom-feed/api"
- "go-common/library/log"
- "go-common/library/net/http/blademaster"
- "go-common/library/net/metadata"
- "go-common/library/sync/errgroup"
- "go-common/library/xstr"
- )
- const (
- _bannerType = 1
- _entranceType = 2
- _yunyingRecFormType = 3
- _yunyingRecSquareType = 4
- _rankType = 5
- _recFormType = 6
- _recSquareType = 7
- _feedType = 8
- _parentAreaFormType = 9
- _parentAreaSquareType = 10
- _activityType = 11
- _myAreaTagType = 12
- _myAreaTagListType = 13
- _seaPatrolType = 14
- )
- // IndexService struct
- type IndexService struct {
- conf *conf.Config
- commonType []int64
- // dao
- dao *dao.Dao
- roomDao *roomDao.Dao
- liveuserDao *liveuserDao.Dao
- rankdbDao *rankdb.Dao
- livedataDao *live_data.Dao
- relationDao *relation.Dao
- roomexDao *room_ex.Dao
- userextDao *user_ext.Dao
- avDao *av.Dao
- fansMedalDao *fans_medal.Dao
- accountDao *account.Dao
- xuserDao *xuserDao.Dao
- // cache
- // all base module cache
- //AllMInfoMap map[int64][]*v2pb.ModuleInfo
- AllMInfoMap atomic.Value
- //areaEntranceMap map[int64][]*v2pb.PicItem
- areaEntranceListMap atomic.Value
- commonRoomList atomic.Value
- recommendConn recommendV1.RecommendClient
- xrfClient *xrf.Client
- }
- // NewIndexService init
- func NewIndexService(c *conf.Config) (s *IndexService) {
- s = &IndexService{
- conf: c,
- dao: dao.New(c),
- roomDao: roomDao.New(c),
- liveuserDao: liveuserDao.New(c),
- rankdbDao: rankdb.New(c),
- roomexDao: roomexDao.New(c),
- accountDao: account.New(c),
- xuserDao: xuserDao.New(c),
- commonType: []int64{
- _yunyingRecFormType,
- _yunyingRecSquareType,
- _recFormType,
- _recSquareType,
- _parentAreaFormType,
- _parentAreaSquareType,
- },
- }
- // init cache data
- s.loadAllModuleInfoMap()
- s.loadAreaEntranceCache()
- s.loadCommonListMap()
- s.loadLastHourData(context.TODO())
- go s.allModuleInfoProc()
- go s.areaEntranceProc()
- go s.allcommonListProc()
- go s.loadLastHour()
- conn, err := recommendV1.NewClient(conf.Conf.Warden)
- if err != nil {
- panic(err)
- }
- s.recommendConn = conn
- xrfc, err := xrf.NewClient(conf.Conf.Warden)
- if err != nil {
- panic(err)
- }
- s.xrfClient = xrfc
- return s
- }
- // Index 相关服务
- // GetAllList implementation
- // 首页大接口
- // `midware:"guest,verify"`
- func (s *IndexService) GetAllList(ctx context.Context, req *v2pb.GetAllListReq) (resp *v2pb.GetAllListResp, err error) {
- resp = &v2pb.GetAllListResp{
- Interval: 10,
- IsSkyHorseGray: 0,
- Banner: []*v2pb.MBanner{},
- MyTag: []*v2pb.MMyTag{},
- AreaEntrance: []*v2pb.MAreaEntrance{},
- SeaPatrol: []*v2pb.MSeaPatrol{},
- MyIdol: []*v2pb.MMyIdol{},
- RoomList: []*v2pb.MRoomBlock{},
- HourRank: []*v2pb.MHourRank{},
- ActivityCard: []*v2pb.MActivityCard{},
- }
- moduleInfoMap := s.GetAllModuleInfoMapFromCache(ctx)
- if moduleInfoMap == nil {
- log.Error("[GetAllList]module info list is nil, moduleIds:%+v", moduleInfoMap)
- return
- }
- // 初始化各模块返回信息
- respCommonRoomList := make([]*v2pb.MRoomBlock, 0)
- respMultiRoomList := make([]*v2pb.MRoomBlock, 0)
- myAreaMap := make(map[int64]bool)
- respMyIdol := &v2pb.MMyIdol{}
- respSkyHorseRoomList := make([]*v2pb.CommonRoomItem, 0)
- respLiveRecRoomList := make([]*v2pb.CommonRoomItem, 0)
- // 大多使用header里的mid解析, 框架已封装请求的header
- midInterface, isUIDSet := metadata.Value(ctx, metadata.Mid).(int64)
- mid := int64(0)
- if isUIDSet {
- mid = midInterface
- }
- buvid := ""
- // 主站封好的,可从device里获取到sid、buvid、buvid3、build、channel、device、mobi_app、platform
- device, ok := metadata.Value(ctx, metadata.Device).(*blademaster.Device)
- if ok {
- buvid = device.Buvid
- }
- // 第一波并发获取数据,无依赖
- func(device *blademaster.Device) {
- wg1 := errgroup.Group{}
- // banner
- if s.isModuleExist(_bannerType) {
- wg1.Go(func() (err error) {
- resp.Banner = s.getIndexBanner(ctx, req.Platform, req.Device, req.Build)
- return
- })
- }
- // 常用标签列表
- if s.isModuleExist(_myAreaTagType) {
- wg1.Go(func() (err error) {
- resp.MyTag, _ = s.GetIndexV2TagList(ctx, &liveUserV1.UserSettingGetTagReq{})
- return
- })
- }
- // 分区入口
- if s.isModuleExist(_entranceType) {
- wg1.Go(func() (err error) {
- resp.AreaEntrance = s.getAreaEntrance(ctx)
- return
- })
- }
- // 我的关注
- if s.isModuleExist(_feedType) {
- wg1.Go(func() (err error) {
- resp.MyIdol = s.LiveAnchorHomePage(ctx, req.RelationPage, req.Build, req.Platform, req.Quality)
- if len(resp.MyIdol) > 0 {
- respMyIdol = resp.MyIdol[0]
- }
- return
- })
- }
- // 通用房间列表(肯定是有的,此处不做判断),推荐、运营推荐分区、常用分区(特殊:要在第二波拿)、一级分区
- wg1.Go(func() (err error) {
- respCommonRoomList = s.getCommonRoomListForIndex(ctx, req.Build, req.Platform, req.Quality)
- return
- })
- // 活动卡片
- if s.isModuleExist(_activityType) {
- wg1.Go(func() (err error) {
- resp.ActivityCard = s.getActivityCard(ctx)
- return
- })
- }
- // 小时榜
- if s.isModuleExist(_rankType) {
- wg1.Go(func() (err error) {
- resp.HourRank, _ = s.getLastHourTop3(ctx)
- return
- })
- }
- // 大航海
- mobiApp := device.RawMobiApp
- if s.isModuleExist(_seaPatrolType) && mobiApp != "iphone_b" && mobiApp != "android_b" {
- wg1.Go(func() (err error) {
- resp.SeaPatrol, _ = s.GetIndexV2SeaPatrol(ctx, &liveUserV1.NoteGetReq{})
- return
- })
- }
- err1 := wg1.Wait()
- if err1 != nil {
- log.Error("[GetAllList]wg1 wait error: %+v", err1)
- }
- }(device)
- // 第二波获取数据 (依赖第一波)
- func() {
- wg2 := errgroup.Group{}
- // 天马个性化推荐 无法缓存 依赖关注
- if s.ifHitSkyHorse(mid, req.Device) {
- wg2.Go(func() (err error) {
- //目前只对第一个关注模块去重
- respSkyHorseRoomList, err = s.getSkyHorseRoomListForIndex(ctx, respMyIdol, mid, buvid, req.Build, req.Platform, req.RecPage, req.Quality)
- return
- })
- }
- // 直播个性化推荐 无法缓存 依赖关注
- if s.ifHitLiveRec(mid, req.Device) {
- wg2.Go(func() (err error) {
- respLiveRecRoomList, err = s.getLiveRecRoomList(ctx, respMyIdol, mid, req.Build, req.Platform, req.RecPage, req.Quality)
- return
- })
- }
- // 常用标签房间列表
- wg2.Go(func() (err error) {
- respMultiRoomList, myAreaMap = s.getMultiRoomList(ctx, resp.MyTag, req.Platform, req.Build, req.Quality)
- resp.RoomList = append(resp.RoomList, respMultiRoomList...)
- return
- })
- // 对保底逻辑的一些处理,对关注去重, 数量限制, 获取投放系统数据
- wg2.Go(func() (err error) {
- respCommonRoomList = s.handleCommonRoomList(ctx, respMyIdol, respCommonRoomList, req.Quality, req.Build, req.Platform, req.Device)
- return
- })
- err2 := wg2.Wait()
- if err2 != nil {
- log.Error("[GetAllList]wg2 wait error: %+v", err2)
- }
- }()
- // 推荐直播最终处理
- handleRecResult(resp, respCommonRoomList, respSkyHorseRoomList, respLiveRecRoomList, myAreaMap)
- if resp.IsSkyHorseGray == 0 && s.ifHitLiveRec(mid, req.Device) {
- log.Info("live rec hit miss, mid:%d, liveRec:%+v", mid, respLiveRecRoomList)
- }
- return
- }
- // Change implementation
- // 换一换接口
- // `midware:"guest,verify"`
- func (s *IndexService) Change(ctx context.Context, req *v2pb.ChangeReq) (resp *v2pb.ChangeResp, err error) {
- resp = &v2pb.ChangeResp{}
- mid, isUIDSet := metadata.Value(ctx, metadata.Mid).(int64)
- var uid int64
- if isUIDSet {
- uid = mid
- }
- duplicates, _ := xstr.SplitInts(req.AttentionRoomId)
- duplicatesMap := make(map[int64]bool)
- for _, roomID := range duplicates {
- duplicatesMap[roomID] = true
- }
- buvid := ""
- // 主站封好的,可从device里获取到sid、buvid、buvid3、build、channel、device、mobi_app、platform
- device, ok := metadata.Value(ctx, metadata.Device).(*blademaster.Device)
- if ok {
- buvid = device.Buvid
- }
- moduleInfo, err := s.roomDao.GetAllModuleInfo(ctx, req.ModuleId)
- if err != nil || moduleInfo[0] == nil {
- log.Error("[Change]GetModuleInfoById error:%+v", err)
- return
- }
- // 给moduleInfo赋值
- resp.ModuleInfo = &v2pb.ModuleInfo{
- Id: moduleInfo[0].Id,
- Link: moduleInfo[0].Link,
- Pic: moduleInfo[0].Pic,
- Title: moduleInfo[0].Title,
- Type: moduleInfo[0].Type,
- Sort: moduleInfo[0].Sort,
- Count: moduleInfo[0].Count,
- }
- resp.List = make([]*v2pb.CommonRoomItem, 0)
- isDefault := true
- if s.ifHitSkyHorse(uid, req.Device) {
- skyHorseList, err := s.getSkyHorseRoomList(ctx, mid, buvid, req.Build, req.Platform, duplicates, req.Page, req.Quality)
- if err == nil && len(skyHorseList) > 0 {
- isDefault = false
- resp.IsSkyHorseGray = 1
- resp.List = skyHorseList
- }
- }
- if s.ifHitLiveRec(mid, req.Device) {
- respLiveRoomList, err := s.getLiveRecRoomListForChange(ctx, mid, req.Build, req.Platform, duplicates, req.Page, req.Quality)
- if err == nil && len(respLiveRoomList) > 0 {
- isDefault = false
- resp.IsSkyHorseGray = 1
- resp.List = respLiveRoomList
- } else {
- log.Info("live rec hit miss, from:change, mid:%d, err:%+v, liveRec:%+v", mid, err, respLiveRoomList)
- }
- }
- if isDefault {
- resp.IsSkyHorseGray = 0
- respCommonRoomList, errTemp := s.getCommonRoomListByID(ctx, req.ModuleId, req.Build, req.Platform, req.Quality, req.Device, duplicates)
- if errTemp != nil {
- log.Error("[Change]GetModuleInfoById error:%+v", errTemp)
- err = errTemp
- return
- }
- resp.List = respCommonRoomList
- }
- return
- }
- // 指定模块是否存在
- func (s *IndexService) isModuleExist(iType int64) (res bool) {
- res = false
- mInfoMap := s.GetAllModuleInfoMapFromCache(context.TODO())
- if _, ok := mInfoMap[iType]; ok {
- res = true
- }
- return
- }
- // 推荐模块最终处理:天马、对关注去重
- func handleRecResult(resp *v2pb.GetAllListResp, respCommonRoomList []*v2pb.MRoomBlock, respSkyHorseRoomList []*v2pb.CommonRoomItem, respLiveRecRoomList []*v2pb.CommonRoomItem, myAreaMap map[int64]bool) {
- afterHandleRoomList := make([]*v2pb.MRoomBlock, 0)
- for _, roomBlock := range respCommonRoomList {
- if roomBlock.ModuleInfo.Type == _recFormType || roomBlock.ModuleInfo.Type == _recSquareType {
- if len(respSkyHorseRoomList) > 0 {
- resp.IsSkyHorseGray = 1
- roomBlock.List = respSkyHorseRoomList
- } else if len(respLiveRecRoomList) > 0 {
- resp.IsSkyHorseGray = 1
- roomBlock.List = respLiveRecRoomList
- }
- afterHandleRoomList = append(afterHandleRoomList, roomBlock)
- continue
- }
- // 常用分区对运营推荐分区去重
- if roomBlock.ModuleInfo.Type == _yunyingRecFormType || roomBlock.ModuleInfo.Type == _yunyingRecSquareType {
- for _, item := range roomBlock.List {
- if _, ok := myAreaMap[item.AreaV2Id]; !ok {
- afterHandleRoomList = append(afterHandleRoomList, roomBlock)
- break
- }
- }
- continue
- }
- afterHandleRoomList = append(afterHandleRoomList, roomBlock)
- }
- resp.RoomList = append(resp.RoomList, afterHandleRoomList...)
- }
- // get AllModuleInfoMap
- func (s *IndexService) getAllModuleInfoMap(ctx context.Context) (allMInfoCacheMap map[int64][]*v2pb.ModuleInfo) {
- var allMInfoData []*roomV2.AppIndexGetBaseMInfoListResp_ModuleInfo
- var err error
- var retry int64
- for i := 1; i <= 3; i++ {
- // 最多重试3次
- allMInfoData, err = s.roomDao.GetAllModuleInfo(ctx, 0)
- if err != nil || len(allMInfoData) <= 0 {
- retry++
- log.Error("[loadAllModuleInfoMap] GetAllModuleInfo error(%+v) retry_times(%d)", err, retry)
- continue
- }
- break
- }
- if len(allMInfoData) > 0 && allMInfoData[1] != nil {
- allMInfoCacheMap = make(map[int64][]*v2pb.ModuleInfo)
- for _, m := range allMInfoData {
- allMInfoCacheMap[m.Type] = append(allMInfoCacheMap[m.Type], &v2pb.ModuleInfo{
- Id: m.Id,
- Link: m.Link,
- Pic: m.Pic,
- Title: m.Title,
- Type: m.Type,
- Sort: m.Sort,
- Count: m.Count,
- })
- }
- }
- return
- }
- // cache load
- func (s *IndexService) loadAllModuleInfoMap() {
- allMInfoCacheMap := s.getAllModuleInfoMap(context.TODO())
- if len(allMInfoCacheMap) > 0 {
- s.AllMInfoMap.Store(allMInfoCacheMap)
- log.Info("[loadAllModuleInfoMap]load data success!")
- }
- }
- // ticker
- func (s *IndexService) allModuleInfoProc() {
- for {
- time.Sleep(time.Second * 5)
- s.loadAllModuleInfoMap()
- }
- }
- // GetAllModuleInfoMapFromCache get all module info fromcache
- func (s *IndexService) GetAllModuleInfoMapFromCache(ctx context.Context) (res map[int64][]*v2pb.ModuleInfo) {
- // load
- i := s.AllMInfoMap.Load()
- // assert
- res, ok := i.(map[int64][]*v2pb.ModuleInfo)
- if ok {
- return
- }
- // 回源&log
- res = s.getAllModuleInfoMap(ctx)
- log.Warn("[GetAllModuleInfoMap]memory cache miss!! i:%+v; res:%+v", i, res)
- return
- }
- func (s *IndexService) getCommonListFromCache(sIds []int64) (commonList map[int64]*roomV2.AppIndexGetRoomListByIdsResp_RoomList) {
- commonListCache, ok := s.commonRoomList.Load().(map[int64]*roomV2.AppIndexGetRoomListByIdsResp_RoomList)
- if ok {
- commonList = commonListCache
- return
- }
- roomListMap, err := s.roomDao.GetListByIds(context.TODO(), sIds)
- if err != nil {
- log.Error("[getCommonListFromCache]GetListByIds error, error:%+v", err)
- return
- }
- commonList = roomListMap
- log.Warn("[getCommonListFromCache]memory cache miss!! res:%+v", commonList)
- return
- }
- func (s *IndexService) loadCommonListMap() {
- sIds := s.getIdsFromModuleMap(context.TODO(), s.commonType)
- roomListMap, err := s.roomDao.GetListByIds(context.TODO(), sIds)
- if err != nil {
- log.Error("[loadCommonListMap]GetListByIds error, error:%+v", err)
- return
- }
- copyRoomListMap := make(map[int64]*roomV2.AppIndexGetRoomListByIdsResp_RoomList)
- for moduleId, roomList := range roomListMap {
- for _, item := range roomList.List {
- if _, ok := copyRoomListMap[moduleId]; !ok {
- copyRoomListMap[moduleId] = &roomV2.AppIndexGetRoomListByIdsResp_RoomList{
- List: make([]*roomV2.AppIndexGetRoomListByIdsResp_RoomInfo, 0),
- }
- }
- copyRoomListMap[moduleId].List = append(copyRoomListMap[moduleId].List, item)
- }
- }
- s.commonRoomList.Store(copyRoomListMap)
- }
- func (s *IndexService) allcommonListProc() {
- for {
- time.Sleep(time.Second * 2)
- s.loadCommonListMap()
- }
- }
- // 根据type从模块信息map拿到模块ids列表
- func (s *IndexService) getIdsFromModuleMap(ctx context.Context, iTypes []int64) (sIds []int64) {
- mMap := s.GetAllModuleInfoMapFromCache(ctx)
- sIds = make([]int64, 0)
- for _, iType := range iTypes {
- typeList, ok := mMap[iType]
- if !ok {
- continue
- }
- for _, item := range typeList {
- if item != nil {
- sIds = append(sIds, item.Id)
- }
- }
- }
- return
- }
|