package feed import ( "context" "encoding/json" "fmt" "time" cdm "go-common/app/interface/main/app-card/model" "go-common/app/interface/main/app-card/model/card/ai" "go-common/app/interface/main/app-card/model/card/audio" "go-common/app/interface/main/app-card/model/card/bangumi" "go-common/app/interface/main/app-card/model/card/banner" "go-common/app/interface/main/app-card/model/card/cm" "go-common/app/interface/main/app-card/model/card/live" "go-common/app/interface/main/app-card/model/card/operate" "go-common/app/interface/main/app-card/model/card/rank" "go-common/app/interface/main/app-card/model/card/show" "go-common/app/interface/main/app-feed/model" "go-common/app/interface/main/app-feed/model/feed" bustag "go-common/app/interface/main/tag/model" article "go-common/app/interface/openplatform/article/model" account "go-common/app/service/main/account/model" "go-common/app/service/main/archive/model/archive" locmdl "go-common/app/service/main/location/model" relation "go-common/app/service/main/relation/model" episodegrpc "go-common/app/service/openplatform/pgc-season/api/grpc/episode/v1" "go-common/library/ecode" "go-common/library/log" "go-common/library/net/metadata" "go-common/library/sync/errgroup" ) const ( _cardAdAv = 1 _cardAdWeb = 2 _cardAdWebS = 3 _cardAdLarge = 7 _feedgroups = "tianma2.0_autoplay_card" ) // Index is func (s *Service) Index(c context.Context, mid int64, plat int8, build int, buvid, network, mobiApp, device, platform, openEvent string, loginEvent int, idx int64, pull bool, now time.Time, bannerHash, adExtra string, qn int, interest string, style, flush, fnver, fnval, autoplayCard int) (is []*feed.Item, userFeature json.RawMessage, isRcmd, newUser bool, code, clean int, autoPlayInfoc string, err error) { var ( ris []*ai.Item adm map[int]*cm.AdInfo adAidm map[int64]struct{} hasBanner bool bs []*banner.Banner version string blackAidm map[int64]struct{} adInfom map[int]*cm.AdInfo follow *operate.Follow autoPlay int ip = metadata.String(c, metadata.RemoteIP) info *locmdl.Info ) //abtest================ // if mid > 0 && mid%20 == 19 { // clean = 1 // } else { // clean = 0 // } clean = 0 if ab, ok := s.abtestCache[_feedgroups]; ok { if ab.AbTestIn(buvid + _feedgroups) { switch autoplayCard { case 0, 1, 2, 3: autoPlay = 1 default: autoPlay = 2 } } else { autoPlay = 2 } } else { switch autoplayCard { case 1, 3: autoPlay = 1 default: autoPlay = 2 } } autoPlayInfoc = fmt.Sprintf("%d|%d", autoPlay, autoplayCard) if info, err = s.loc.Info(c, ip); err != nil { log.Warn("s.loc.Info(%v) error(%v)", ip, err) err = nil } //abtest================ group := s.group(mid, buvid) g, ctx := errgroup.WithContext(c) g.Go(func() error { ris, userFeature, isRcmd, newUser, code = s.indexRcmd(ctx, plat, build, buvid, mid, group, loginEvent, 0, info, interest, network, style, -1, flush, autoPlayInfoc, now) return nil }) // 暂停实验 // if !((group == 18 || group == 19) && style == 3) { g.Go(func() (err error) { if adm, adAidm, err = s.indexAd(ctx, plat, build, buvid, mid, network, mobiApp, device, openEvent, info, now, adExtra, style); err != nil { log.Error("%+v", err) err = nil } return }) // } g.Go(func() (err error) { if hasBanner, bs, version, err = s.indexBanner(ctx, plat, build, buvid, mid, loginEvent, bannerHash, network, mobiApp, device, "", adExtra); err != nil { log.Error("%+v", err) err = nil } return }) g.Go(func() (err error) { if blackAidm, err = s.BlackList(ctx, mid); err != nil { log.Error("%+v", err) err = nil } return }) g.Go(func() (err error) { if follow, err = s.SearchFollow(ctx, platform, mobiApp, device, buvid, build, mid); err != nil { log.Error("%+v", err) err = nil } return }) if err = g.Wait(); err != nil { return } ris, adInfom = s.mergeItem(c, mid, ris, adm, adAidm, hasBanner, blackAidm, plat, follow) is, isRcmd, err = s.dealItem(c, mid, plat, build, buvid, ris, bs, version, isRcmd, network, mobiApp, device, openEvent, idx, pull, qn, now, adExtra, adInfom, fnver, fnval, autoPlay, follow) return } // Dislike is. func (s *Service) Dislike(c context.Context, mid, id int64, buvid, gt string, reasonID, cmreasonID, feedbackID, upperID, rid, tagID int64, adcb string, now time.Time) (err error) { if gt == model.GotoAv { s.blk.AddBlacklist(mid, id) } return s.rcmd.PubDislike(c, buvid, gt, id, mid, reasonID, cmreasonID, feedbackID, upperID, rid, tagID, adcb, now) } // DislikeCancel is. func (s *Service) DislikeCancel(c context.Context, mid, id int64, buvid, gt string, reasonID, cmreasonID, feedbackID, upperID, rid, tagID int64, adcb string, now time.Time) (err error) { if gt == model.GotoAv { s.blk.DelBlacklist(mid, id) } return s.rcmd.PubDislikeCancel(c, buvid, gt, id, mid, reasonID, cmreasonID, feedbackID, upperID, rid, tagID, adcb, now) } func (s *Service) indexRcmd(c context.Context, plat int8, build int, buvid string, mid int64, group int, loginEvent, parentMode int, zone *locmdl.Info, interest, network string, style int, column cdm.ColumnStatus, flush int, autoPlay string, now time.Time) (is []*ai.Item, userFeature json.RawMessage, isRcmd, newUser bool, code int) { count := s.indexCount(plat) if buvid != "" || mid != 0 { var ( err error zoneID int64 ) if zone != nil { zoneID = zone.ZoneID } if is, userFeature, code, newUser, err = s.rcmd.Recommend(c, plat, buvid, mid, build, loginEvent, parentMode, 0, zoneID, group, interest, network, style, column, flush, autoPlay, now); err != nil { log.Error("%+v", err) } else if len(is) != 0 { isRcmd = true } var fromCache bool if len(is) == 0 && mid != 0 && !ecode.ServiceUnavailable.Equal(err) { if is, err = s.indexCache(c, mid, count); err != nil { log.Error("%+v", err) } if len(is) != 0 { s.pHit.Incr("index_cache") } else { s.pMiss.Incr("index_cache") } fromCache = true } if len(is) == 0 || (fromCache && len(is) < count) { is = s.recommendCache(count) } } else { is = s.recommendCache(count) } return } func (s *Service) indexAd(c context.Context, plat int8, build int, buvid string, mid int64, network, mobiApp, device, openEvent string, zone *locmdl.Info, now time.Time, adExtra string, style int) (adm map[int]*cm.AdInfo, adAidm map[int64]struct{}, err error) { var advert *cm.Ad resource := s.adResource(plat, build) if resource == 0 { return } // 兼容老的style逻辑,3为新单列,上报给商业产品的参数定义为:1 单列 2双列 if style == 3 { style = 1 } var country, province, city string if zone != nil { country = zone.Country province = zone.Province city = zone.City } if advert, err = s.ad.Ad(c, mid, build, buvid, []int64{resource}, country, province, city, network, mobiApp, device, openEvent, adExtra, style, now); err != nil { return } if advert == nil || len(advert.AdsInfo) == 0 { return } if adsInfo, ok := advert.AdsInfo[resource]; ok { adm = make(map[int]*cm.AdInfo, len(adsInfo)) adAidm = make(map[int64]struct{}, len(adsInfo)) for source, info := range adsInfo { if info == nil { continue } var adInfo *cm.AdInfo if info.AdInfo != nil { adInfo = info.AdInfo adInfo.RequestID = advert.RequestID adInfo.Resource = resource adInfo.Source = source adInfo.IsAd = info.IsAd adInfo.IsAdLoc = true adInfo.CmMark = info.CmMark adInfo.Index = info.Index adInfo.CardIndex = info.CardIndex adInfo.ClientIP = advert.ClientIP if adInfo.CreativeID != 0 && adInfo.CardType == _cardAdAv { adAidm[adInfo.CreativeContent.VideoID] = struct{}{} } } else { adInfo = &cm.AdInfo{RequestID: advert.RequestID, Resource: resource, Source: source, IsAdLoc: true, IsAd: info.IsAd, CmMark: info.CmMark, Index: info.Index, CardIndex: info.CardIndex, ClientIP: advert.ClientIP} } adm[adInfo.CardIndex-1] = adInfo } } return } func (s *Service) indexBanner(c context.Context, plat int8, build int, buvid string, mid int64, loginEvent int, hash, network, mobiApp, device, openEvent, adExtra string) (has bool, bs []*banner.Banner, version string, err error) { const ( _androidBanBannerHash = 515009 _iphoneBanBannerHash = 6120 _ipadBanBannerHash = 6160 ) if (plat == model.PlatAndroid && build > _androidBanBannerHash) || (plat == model.PlatIPhone && build > _iphoneBanBannerHash) || (plat == model.PlatIPad && build > _ipadBanBannerHash) || loginEvent != 0 { if bs, version, err = s.banners(c, plat, build, mid, buvid, network, mobiApp, device, openEvent, adExtra, ""); err != nil { return } else if loginEvent != 0 { has = true } else if version != "" { has = hash != version } } return } func (s *Service) mergeItem(c context.Context, mid int64, rs []*ai.Item, adm map[int]*cm.AdInfo, adAidm map[int64]struct{}, hasBanner bool, blackAids map[int64]struct{}, plat int8, follow *operate.Follow) (is []*ai.Item, adInfom map[int]*cm.AdInfo) { if len(rs) == 0 { return } const ( cardIndex = 7 cardIndexIPad = 17 cardOffset = 2 ) if hasBanner { rs = append([]*ai.Item{&ai.Item{Goto: model.GotoBanner}}, rs...) for index, ad := range adm { if ((model.IsIPad(plat) && index <= cardIndexIPad) || index <= cardIndex) && (ad.CardType == _cardAdWeb || ad.CardType == _cardAdLarge) { ad.CardIndex = ad.CardIndex + cardOffset } } } if follow != nil { followPos := s.c.Feed.Index.FollowPosition if followPos-1 >= 0 && followPos-1 <= len(rs) { rs = append(rs[:followPos-1], append([]*ai.Item{&ai.Item{ID: follow.ID, Goto: model.GotoSearchSubscribe}}, rs[followPos-1:]...)...) } } is = make([]*ai.Item, 0, len(rs)+len(adm)) adInfom = make(map[int]*cm.AdInfo, len(adm)) var existsBanner, existsAdWeb bool for _, r := range rs { for { if ad, ok := adm[len(is)]; ok { if ad.CreativeID != 0 { var item *ai.Item if ad.CardType == _cardAdAv { item = &ai.Item{ID: ad.CreativeContent.VideoID, Goto: model.GotoAdAv, Ad: ad} } else if ad.CardType == _cardAdWeb { item = &ai.Item{Goto: model.GotoAdWeb, Ad: ad} existsAdWeb = true } else if ad.CardType == _cardAdWebS { item = &ai.Item{Goto: model.GotoAdWebS, Ad: ad} } else if ad.CardType == _cardAdLarge { item = &ai.Item{Goto: model.GotoAdLarge, Ad: ad} } else { b, _ := json.Marshal(ad) log.Error("ad---%s", b) break } is = append(is, item) continue } else { adInfom[len(is)] = ad } } break } if r.Goto == model.GotoAv { if _, ok := blackAids[r.ID]; ok { continue } else if _, ok := s.blackCache[r.ID]; ok { continue } if _, ok := adAidm[r.ID]; ok { continue } } else if r.Goto == model.GotoBanner { if existsBanner { continue } else { existsBanner = true } } else if r.Goto == model.GotoRank && existsAdWeb { continue } else if r.Goto == model.GotoLogin && mid != 0 { continue } is = append(is, r) } return } func (s *Service) dealItem(c context.Context, mid int64, plat int8, build int, buvid string, rs []*ai.Item, bs []*banner.Banner, version string, isRcmd bool, network, mobiApp, device, openEvent string, idx int64, pull bool, qn int, now time.Time, adExtra string, adInfom map[int]*cm.AdInfo, fnver, fnval, autoPlay int, follow *operate.Follow) (is []*feed.Item, isAI bool, err error) { if len(rs) == 0 { is = _emptyItem return } var ( aids, tids, roomIDs, sids, metaIDs, shopIDs, audioIDs []int64 upIDs, avUpIDs, rmUpIDs, mtUpIDs []int64 seasonIDs []int32 ranks []*rank.Rank am map[int64]*archive.ArchiveWithPlayer tagm map[int64]*bustag.Tag follows map[int64]bool rm map[int64]*live.Room sm map[int64]*bangumi.Season hasBangumiRcmd bool update *bangumi.Update atm map[int64]*article.Meta scm map[int64]*show.Shopping aum map[int64]*audio.Audio hasBanner bool card map[int64]*account.Card upStatm map[int64]*relation.Stat arcOK bool seasonCards map[int32]*episodegrpc.EpisodeCardsProto ) isAI = isRcmd convergem := map[int64]*operate.Converge{} downloadm := map[int64]*operate.Download{} liveUpm := map[int64][]*live.Card{} followm := map[int64]*operate.Follow{} for _, r := range rs { switch r.Goto { case model.GotoAv, model.GotoAdAv, model.GotoPlayer, model.GotoUpRcmdAv: if r.ID != 0 { aids = append(aids, r.ID) } if r.Tid != 0 { tids = append(tids, r.Tid) } case model.GotoLive, model.GotoPlayerLive: if r.ID != 0 { roomIDs = append(roomIDs, r.ID) } case model.GotoBangumi: if r.ID != 0 { sids = append(sids, r.ID) } case model.GotoPGC: if r.ID != 0 { seasonIDs = append(seasonIDs, int32(r.ID)) } case model.GotoRank: card, aid := s.RankCard(model.PlatIPhone) ranks = card aids = append(aids, aid...) case model.GotoBangumiRcmd: hasBangumiRcmd = true case model.GotoBanner: hasBanner = true case model.GotoConverge: if card, ok := s.convergeCache[r.ID]; ok { for _, item := range card.Items { switch item.Goto { case model.GotoAv: if item.Pid != 0 { aids = append(aids, item.Pid) } case model.GotoLive: if item.Pid != 0 { roomIDs = append(roomIDs, item.Pid) } case model.GotoArticle: if item.Pid != 0 { metaIDs = append(metaIDs, item.Pid) } } } convergem[r.ID] = card } case model.GotoGameDownloadS: if card, ok := s.downloadCache[r.ID]; ok { downloadm[r.ID] = card } case model.GotoArticleS: if r.ID != 0 { metaIDs = append(metaIDs, r.ID) } case model.GotoShoppingS: if r.ID != 0 { shopIDs = append(shopIDs, r.ID) } case model.GotoAudio: if r.ID != 0 { audioIDs = append(audioIDs, r.ID) } case model.GotoLiveUpRcmd: if r.ID != 0 { if cs, ok := s.liveCardCache[r.ID]; ok { for _, c := range cs { upIDs = append(upIDs, c.UID) } } } case model.GotoSubscribe: if r.ID != 0 { if card, ok := s.followCache[r.ID]; ok { for _, item := range card.Items { switch item.Goto { case cdm.GotoMid: if item.Pid != 0 { upIDs = append(upIDs, item.Pid) } case cdm.GotoTag: if item.Pid != 0 { tids = append(tids, item.Pid) } } } followm[r.ID] = card } } case model.GotoChannelRcmd: if r.ID != 0 { if card, ok := s.followCache[r.ID]; ok { if card.Pid != 0 { aids = append(aids, card.Pid) } if card.Tid != 0 { tids = append(tids, card.Tid) } followm[r.ID] = card } } case model.GotoSearchSubscribe: if follow != nil { followm[follow.ID] = follow for _, item := range follow.Items { upIDs = append(upIDs, item.Pid) } } } } g, ctx := errgroup.WithContext(c) if len(aids) != 0 { g.Go(func() (err error) { if am, err = s.ArchivesWithPlayer(ctx, aids, qn, mobiApp, fnver, fnval, 0, build); err != nil { return } arcOK = true for _, a := range am { avUpIDs = append(avUpIDs, a.Author.Mid) } return }) } if len(tids) != 0 { g.Go(func() (err error) { if tagm, err = s.tg.InfoByIDs(ctx, mid, tids); err != nil { log.Error("%+v", err) err = nil } return }) } if len(roomIDs) != 0 { g.Go(func() (err error) { if rm, err = s.lv.AppMRoom(ctx, roomIDs); err != nil { log.Error("%+v", err) err = nil } for _, r := range rm { rmUpIDs = append(rmUpIDs, r.UID) } return }) } if len(sids) != 0 { g.Go(func() (err error) { if sm, err = s.bgm.Seasons(ctx, sids, now); err != nil { log.Error("%+v", err) err = nil } return }) } if len(seasonIDs) != 0 { g.Go(func() (err error) { if seasonCards, err = s.bgm.CardsInfoReply(ctx, seasonIDs); err != nil { log.Error("%+v", err) err = nil } return }) } // TODO DEL // if hasBangumiRcmd && mid != 0 { if hasBangumiRcmd { g.Go(func() (err error) { if update, err = s.bgm.Updates(ctx, mid, now); err != nil { log.Error("%+v", err) err = nil } return }) } if hasBanner && version == "" { g.Go(func() (err error) { if bs, version, err = s.banners(ctx, plat, build, mid, buvid, network, mobiApp, device, openEvent, adExtra, ""); err != nil { log.Error("%+v", err) err = nil } return }) } if len(metaIDs) != 0 { g.Go(func() (err error) { if atm, err = s.art.Articles(ctx, metaIDs); err != nil { log.Error("%+v", err) err = nil } for _, at := range atm { if at.Author != nil { mtUpIDs = append(mtUpIDs, at.Author.Mid) } } return }) } if len(shopIDs) != 0 { g.Go(func() (err error) { if scm, err = s.show.Card(ctx, shopIDs); err != nil { log.Error("%+v", err) err = nil } return }) } if len(audioIDs) != 0 { g.Go(func() (err error) { if aum, err = s.audio.Audios(ctx, audioIDs); err != nil { log.Error("%+v", err) err = nil } return }) } if err = g.Wait(); err != nil { log.Error("%+v", err) if isRcmd { count := s.indexCount(plat) rs = s.recommendCache(count) } } else { upIDs = append(upIDs, avUpIDs...) upIDs = append(upIDs, rmUpIDs...) upIDs = append(upIDs, mtUpIDs...) g, ctx = errgroup.WithContext(c) if len(upIDs) != 0 { g.Go(func() (err error) { if card, err = s.acc.Cards3(ctx, upIDs); err != nil { log.Error("%+v", err) err = nil } return }) g.Go(func() (err error) { if upStatm, err = s.rel.Stats(ctx, upIDs); err != nil { log.Error("%+v", err) err = nil } return }) if mid != 0 { g.Go(func() error { follows = s.acc.Relations3(ctx, upIDs, mid) return nil }) } } g.Wait() } isAI = isAI && arcOK // init feed items is = make([]*feed.Item, 0, len(rs)) var ( smallCardCnt int middleCardCnt int ) ip := metadata.String(c, metadata.RemoteIP) adm := map[int]*feed.Item{} isIpad := plat == model.PlatIPad for _, r := range rs { il := len(is) i := &feed.Item{AI: r} i.FromRcmd(r) switch r.Goto { case model.GotoAv, model.GotoUpRcmdAv: a, ok := am[r.ID] if !ok && !arcOK { a = &archive.ArchiveWithPlayer{Archive3: r.Archive} } isOsea := model.IsOverseas(plat) if a != nil && a.Archive3 != nil && a.IsNormal() && (!isOsea || (isOsea && a.AttrVal(archive.AttrBitOverseaLock) == 0)) { i.FromPlayerAv(a) if arcOK { if info, ok := tagm[r.Tid]; ok { i.Tag = &feed.Tag{TagID: info.ID, TagName: info.Name, IsAtten: info.IsAtten, Count: &feed.TagCount{Atten: info.Count.Atten}} } } else if r.Tag != nil { i.Tag = &feed.Tag{TagID: r.Tag.ID, TagName: r.Tag.Name} } i.FromDislikeReason(plat, build) i.FromRcmdReason(r.RcmdReason) if follows[i.Mid] { i.IsAtten = 1 } if card, ok := card[i.Mid]; ok { if card.Official.Role != 0 { i.Official = &feed.OfficialInfo{Role: card.Official.Role, Title: card.Official.Title, Desc: card.Official.Desc} } } // for GotoUpRcmdAv i.Goto = r.Goto if i.Goto == model.GotoUpRcmdAv { // TODO 等待开启 // percent := i.Like / (i.Like + i.Dislike) * 100 // if percent != 0 { // i.Desc = strconv.Itoa(percent) + "%的人推荐" // } i.Desc = "" } is = append(is, i) smallCardCnt++ } case model.GotoLive: if r, ok := rm[r.ID]; ok { i.FromLive(r) if card, ok := card[i.Mid]; ok { if card.Official.Role != 0 { i.Official = &feed.OfficialInfo{Role: card.Official.Role, Title: card.Official.Title, Desc: card.Official.Desc} } } if stat, ok := upStatm[i.Mid]; ok { i.Fans = stat.Follower } if follows[i.Mid] { i.IsAtten = 1 } if i.Goto != "" { is = append(is, i) smallCardCnt++ } } case model.GotoBangumi: if s, ok := sm[r.ID]; ok { i.FromSeason(s) is = append(is, i) smallCardCnt++ } case model.GotoPGC: if s, ok := seasonCards[int32(r.ID)]; ok { i.FromPGCSeason(s) is = append(is, i) smallCardCnt++ } case model.GotoLogin: i.FromLogin() is = append(is, i) smallCardCnt++ case model.GotoAdAv: if r.Ad != nil { if a, ok := am[r.ID]; ok && model.AdAvIsNormal(a) { i.FromAdAv(r.Ad, a) if follows[i.Mid] { i.IsAtten = 1 } if card, ok := card[i.Mid]; ok { if card.Official.Role != 0 { i.Official = &feed.OfficialInfo{Role: card.Official.Role, Title: card.Official.Title, Desc: card.Official.Desc} } } i.ClientIP = ip adm[i.CardIndex-1] = i } } case model.GotoAdWebS: if r.Ad != nil { i.FromAdWebS(r.Ad) i.ClientIP = ip adm[i.CardIndex-1] = i } case model.GotoAdWeb: if r.Ad != nil { i.FromAdWeb(r.Ad) i.ClientIP = ip adm[i.CardIndex-1] = i } case model.GotoAdLarge: if r.Ad != nil { i.FromAdLarge(r.Ad) i.ClientIP = ip adm[i.CardIndex-1] = i } case model.GotoSpecial: if sc, ok := s.specialCache[r.ID]; ok { i.FromSpecial(sc.ID, sc.Title, sc.Cover, sc.Desc, sc.ReValue, sc.ReType, sc.Badge, sc.Size) } if i.Goto != "" { if !isIpad { if smallCardCnt%2 != 0 { is = swapTwoItem(is, i) } else { is = append(is, i) } } else { if (smallCardCnt+middleCardCnt*2)%2 != 0 { is = swapTwoItem(is, i) } else { is = append(is, i) } middleCardCnt++ } } case model.GotoSpecialS: if sc, ok := s.specialCache[r.ID]; ok { i.FromSpecialS(sc.ID, sc.Title, sc.Cover, sc.SingleCover, sc.Desc, sc.ReValue, sc.ReType, sc.Badge) } if i.Goto != "" { if !isIpad { is = append(is, i) smallCardCnt++ } } case model.GotoRank: i.FromRank(ranks, am) if i.Goto != "" { if !isIpad { if smallCardCnt%2 != 0 { is = swapTwoItem(is, i) } else { is = append(is, i) } } else { if (smallCardCnt+middleCardCnt*2)%2 != 0 { is = swapTwoItem(is, i) } else { is = append(is, i) } middleCardCnt++ } } case model.GotoBangumiRcmd: if mid != 0 && update != nil && update.Updates != 0 { i.FromBangumiRcmd(update) if !isIpad { if smallCardCnt%2 != 0 { is = swapTwoItem(is, i) } else { is = append(is, i) } } else { is = append(is, i) smallCardCnt++ } } case model.GotoBanner: if len(bs) != 0 { i.FromBanner(bs, version) if !isIpad { if smallCardCnt%2 != 0 { is = swapTwoItem(is, i) } else { is = append(is, i) } } else { switch (smallCardCnt + middleCardCnt*2) % 4 { case 0: is = append(is, i) case 1: is = swapTwoItem(is, i) case 2: switch is[len(is)-1].Goto { case model.GotoRank, model.GotoAdWeb, model.GotoAdLarge: is = swapTwoItem(is, i) default: is = swapThreeItem(is, i) } case 3: is = swapThreeItem(is, i) } } } case model.GotoConverge: if cc, ok := convergem[r.ID]; ok { i.FromConverge(cc, am, rm, atm) if i.Goto != "" { if !isIpad { if smallCardCnt%2 != 0 { is = swapTwoItem(is, i) } else { is = append(is, i) } } } } case model.GotoGameDownloadS: if gd, ok := downloadm[r.ID]; ok { i.FromGameDownloadS(gd, plat, build) if i.Goto != "" { if !isIpad { is = append(is, i) smallCardCnt++ } } } case model.GotoArticleS: if m, ok := atm[r.ID]; ok { i.FromArticleS(m) if card, ok := card[i.Mid]; ok { if card.Official.Role != 0 { i.Official = &feed.OfficialInfo{Role: card.Official.Role, Title: card.Official.Title, Desc: card.Official.Desc} } } if i.Goto != "" { if !isIpad { is = append(is, i) smallCardCnt++ } } } case model.GotoShoppingS: if c, ok := scm[r.ID]; ok { i.FromShoppingS(c) if i.Goto != "" { if !isIpad { is = append(is, i) smallCardCnt++ } } } case model.GotoAudio: if au, ok := aum[r.ID]; ok { i.FromAudio(au) is = append(is, i) smallCardCnt++ } case model.GotoPlayer: if a, ok := am[r.ID]; ok { i.FromPlayer(a) if i.Goto != "" { if info, ok := tagm[r.Tid]; ok { i.Tag = &feed.Tag{TagID: info.ID, TagName: info.Name, IsAtten: info.IsAtten, Count: &feed.TagCount{Atten: info.Count.Atten}} } if follows[i.Mid] { i.IsAtten = 1 } if card, ok := card[i.Mid]; ok { if card.Official.Role != 0 { i.Official = &feed.OfficialInfo{Role: card.Official.Role, Title: card.Official.Title, Desc: card.Official.Desc} } } i.FromDislikeReason(plat, build) if !isIpad { if smallCardCnt%2 != 0 { is = swapTwoItem(is, i) } else { is = append(is, i) } } } } case model.GotoPlayerLive: if r, ok := rm[r.ID]; ok { i.FromPlayerLive(r) if i.Goto != "" { if follows[i.Mid] { i.IsAtten = 1 } if card, ok := card[i.Mid]; ok { if card.Official.Role != 0 { i.Official = &feed.OfficialInfo{Role: card.Official.Role, Title: card.Official.Title, Desc: card.Official.Desc} } } if stat, ok := upStatm[i.Mid]; ok { i.Fans = stat.Follower } if !isIpad { if smallCardCnt%2 != 0 { is = swapTwoItem(is, i) } else { is = append(is, i) } } } } case model.GotoSubscribe, model.GotoSearchSubscribe: if c, ok := followm[r.ID]; ok { if !isIpad { i.FromSubscribe(c, card, follows, upStatm, tagm) if i.Goto != "" { if smallCardCnt%2 != 0 { is = swapTwoItem(is, i) } else { is = append(is, i) } } } } case model.GotoChannelRcmd: if c, ok := followm[r.ID]; ok { if !isIpad { i.FromChannelRcmd(c, am, tagm) if i.Goto != "" { if !isIpad { is = append(is, i) smallCardCnt++ } } } } case model.GotoLiveUpRcmd: if c, ok := liveUpm[r.ID]; ok { if !isIpad { i.FromLiveUpRcmd(r.ID, c, card) if i.Goto != "" { if smallCardCnt%2 != 0 { is = swapTwoItem(is, i) } else { is = append(is, i) } } } } default: log.Warn("unexpected goto(%s) %+v", r.Goto, r) continue } if ad, ok := adm[il]; ok { switch ad.Goto { case model.GotoAdAv, model.GotoAdWebS: is = append(is, ad) smallCardCnt++ case model.GotoAdWeb, model.GotoAdLarge: if !isIpad { if smallCardCnt%2 != 0 { is = swapTwoItem(is, ad) } else { is = append(is, ad) } } else { if (smallCardCnt+middleCardCnt*2)%2 != 0 { is = swapTwoItem(is, ad) } else { is = append(is, ad) } middleCardCnt++ } } } } if !isIpad { is = is[:len(is)-smallCardCnt%2] } else { switch (smallCardCnt + middleCardCnt*2) % 4 { case 1: is = is[:len(is)-1] case 2: if isMiddleCard(is[len(is)-1].Goto) { is = is[:len(is)-1] } else { is = is[:len(is)-2] } case 3: if isMiddleCard(is[len(is)-1].Goto) { is = is[:len(is)-2] } else { is = is[:len(is)-3] } } } rl := len(is) if rl == 0 { is = _emptyItem return } if idx == 0 { idx = now.Unix() } for i, r := range is { if pull { r.Idx = idx + int64(rl-i) } else { r.Idx = idx - int64(i+1) } if ad, ok := adInfom[i]; ok { r.SrcID = ad.Source r.RequestID = ad.RequestID r.IsAdLoc = ad.IsAdLoc r.IsAd = ad.IsAd r.CmMark = ad.CmMark r.AdIndex = ad.Index r.ClientIP = ip r.CardIndex = i + 1 } else if r.IsAd { r.CardIndex = i + 1 } if i == 0 { r.AutoplayCard = autoPlay } } return } func (s *Service) adResource(plat int8, build int) (resource int64) { const ( _androidBanAd = 500001 ) if plat == model.PlatIPhone || plat == model.PlatIPhoneB || (plat == model.PlatAndroid && build >= _androidBanAd) || plat == model.PlatIPad { resource = s.cmResourceMap[plat] } return } func swapTwoItem(rs []*feed.Item, i *feed.Item) (is []*feed.Item) { rs[len(rs)-1].Idx, i.Idx = i.Idx, rs[len(rs)-1].Idx is = append(rs, rs[len(rs)-1]) is[len(is)-2] = i return } func swapThreeItem(rs []*feed.Item, i *feed.Item) (is []*feed.Item) { rs[len(rs)-1].Idx, i.Idx = i.Idx, rs[len(rs)-1].Idx rs[len(rs)-2].Idx, rs[len(is)-1].Idx = rs[len(rs)-1].Idx, rs[len(rs)-2].Idx is = append(rs, rs[len(rs)-1]) is[len(is)-2] = i is[len(is)-3], is[len(is)-2] = is[len(is)-2], is[len(is)-3] return } func isMiddleCard(gt string) bool { return gt == model.GotoRank || gt == model.GotoAdWeb || gt == model.GotoPlayer || gt == model.GotoPlayerLive || gt == model.GotoConverge || gt == model.GotoSpecial || gt == model.GotoAdLarge || gt == model.GotoLiveUpRcmd } func (s *Service) indexCount(plat int8) (count int) { if plat == model.PlatIPad { count = s.c.Feed.Index.IPadCount } else { count = s.c.Feed.Index.Count } return }