1089 lines
29 KiB
Go
1089 lines
29 KiB
Go
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
|
||
}
|