You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1004 lines
28 KiB
1004 lines
28 KiB
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/bplus" |
|
"go-common/app/interface/main/app-card/model/card" |
|
"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/show" |
|
"go-common/app/interface/main/app-feed/model" |
|
"go-common/app/interface/main/app-feed/model/feed" |
|
tag "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 _qn480 = 32 |
|
|
|
var ( |
|
_cardAdAvm = map[int]struct{}{ |
|
1: struct{}{}, |
|
} |
|
_cardAdWebm = map[int]struct{}{ |
|
2: struct{}{}, |
|
7: struct{}{}, |
|
20: struct{}{}, |
|
} |
|
_cardAdWebSm = map[int]struct{}{ |
|
3: struct{}{}, |
|
26: struct{}{}, |
|
} |
|
_followMode = &feed.FollowMode{ |
|
Title: "当前为首页推荐 - 关注模式(内测版)", |
|
Option: []*feed.Option{ |
|
{Title: "通用模式", Desc: "开启后,推荐你可能感兴趣的内容", Value: 0}, |
|
{Title: "关注模式(内测版)", Desc: "开启后,仅显示关注UP主更新的视频", Value: 1}, |
|
}, |
|
ToastMessage: "关注UP主的内容已经看完啦,请稍后再试", |
|
} |
|
) |
|
|
|
func (s *Service) Index2(c context.Context, buvid string, mid int64, plat int8, param *feed.IndexParam, style int, now time.Time) (is []card.Handler, config *feed.Config, infoc *feed.Infoc, err error) { |
|
var ( |
|
rs []*ai.Item |
|
adm map[int]*cm.AdInfo |
|
adAidm map[int64]struct{} |
|
banners []*banner.Banner |
|
version string |
|
blackAidm map[int64]struct{} |
|
adInfom map[int]*cm.AdInfo |
|
follow *operate.Card |
|
info *locmdl.Info |
|
) |
|
ip := metadata.String(c, metadata.RemoteIP) |
|
config = s.indexConfig(c, plat, buvid, mid, param) |
|
if config.FollowMode == nil { |
|
param.RecsysMode = 0 |
|
} |
|
noCache := param.RecsysMode == 1 |
|
followMode := config.FollowMode != nil |
|
infoc = &feed.Infoc{} |
|
infoc.AutoPlayInfoc = fmt.Sprintf("%d|%d", config.AutoplayCard, param.AutoPlayCard) |
|
if info, err = s.loc.Info(c, ip); err != nil { |
|
log.Warn("s.loc.Info(%v) error(%v)", ip, err) |
|
err = nil |
|
} |
|
group := s.group(mid, buvid) |
|
if !s.c.Feed.Index.Abnormal || followMode { |
|
g, ctx := errgroup.WithContext(c) |
|
g.Go(func() error { |
|
rs, infoc.UserFeature, infoc.IsRcmd, infoc.NewUser, infoc.Code = s.indexRcmd2(ctx, plat, buvid, mid, param, group, info, style, infoc.AutoPlayInfoc, noCache, now) |
|
return nil |
|
}) |
|
g.Go(func() (err error) { |
|
if banners, version, err = s.indexBanner2(ctx, plat, buvid, mid, param); err != nil { |
|
log.Error("%+v", err) |
|
err = nil |
|
} |
|
return |
|
}) |
|
if param.RecsysMode == 0 { |
|
g.Go(func() (err error) { |
|
if adm, adAidm, err = s.indexAd2(ctx, plat, buvid, mid, param, info, style, now); 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.SearchFollow2(ctx, param.Platform, param.MobiApp, param.Device, buvid, param.Build, mid); err != nil { |
|
log.Error("%+v", err) |
|
err = nil |
|
} |
|
return |
|
}) |
|
} |
|
if err = g.Wait(); err != nil { |
|
log.Error("%+v", err) |
|
return |
|
} |
|
if param.RecsysMode == 1 { |
|
var tmp []*ai.Item |
|
for _, r := range rs { |
|
if r.Goto == model.GotoBanner { |
|
continue |
|
} |
|
tmp = append(tmp, r) |
|
} |
|
if len(tmp) == 0 { |
|
is = []card.Handler{} |
|
return |
|
} |
|
} |
|
rs, adInfom = s.mergeItem2(c, plat, mid, rs, adm, adAidm, banners, version, blackAidm, follow, followMode) |
|
} else { |
|
count := s.indexCount(plat) |
|
rs = s.recommendCache(count) |
|
log.Warn("feed index show disaster recovery data len(%d)", len(is)) |
|
} |
|
if config.AutoplayCard == 1 && cdm.Columnm[param.Column] == cdm.ColumnSvrSingle { |
|
param.Qn = _qn480 |
|
} |
|
is, infoc.IsRcmd = s.dealItem2(c, mid, buvid, plat, rs, param, infoc.IsRcmd, noCache, followMode, follow, now) |
|
s.dealAdLoc(is, param, adInfom, now) |
|
return |
|
} |
|
|
|
func (s *Service) indexConfig(c context.Context, plat int8, buvid string, mid int64, param *feed.IndexParam) (config *feed.Config) { |
|
config = &feed.Config{} |
|
config.Column = cdm.Columnm[param.Column] |
|
// if mid > 0 && mid%20 == 19 { |
|
// config.FeedCleanAbtest = 1 |
|
// } else { |
|
// config.FeedCleanAbtest = 0 |
|
// } |
|
config.FeedCleanAbtest = 0 |
|
if !model.IsIPad(plat) { |
|
if ab, ok := s.abtestCache[_feedgroups]; ok { |
|
if ab.AbTestIn(buvid + _feedgroups) { |
|
switch param.AutoPlayCard { |
|
case 0, 1, 2, 3: |
|
config.AutoplayCard = 1 |
|
default: |
|
config.AutoplayCard = 2 |
|
} |
|
} else { |
|
config.AutoplayCard = 2 |
|
} |
|
} else { |
|
switch param.AutoPlayCard { |
|
case 1, 3: |
|
config.AutoplayCard = 1 |
|
default: |
|
config.AutoplayCard = 2 |
|
} |
|
} |
|
} else { |
|
// ipad 不允许自动播放 |
|
config.AutoplayCard = 2 |
|
} |
|
if mid < 1 { |
|
return |
|
} |
|
if _, ok := s.autoplayMidsCache[mid]; ok && param.AutoPlayCard != 4 { |
|
config.AutoplayCard = 1 |
|
} |
|
if _, ok := s.followModeList[mid]; ok { |
|
tmpConfig := &feed.FollowMode{} |
|
if s.c.Feed.Index.FollowMode == nil { |
|
*tmpConfig = *_followMode |
|
} else { |
|
*tmpConfig = *s.c.Feed.Index.FollowMode |
|
} |
|
if param.RecsysMode != 1 { |
|
tmpConfig.ToastMessage = "" |
|
} |
|
config.FollowMode = tmpConfig |
|
} |
|
return |
|
} |
|
|
|
func (s *Service) indexRcmd2(c context.Context, plat int8, buvid string, mid int64, param *feed.IndexParam, group int, zone *locmdl.Info, style int, autoPlay string, noCache bool, 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, param.Build, param.LoginEvent, param.ParentMode, param.RecsysMode, zoneID, group, param.Interest, param.Network, style, param.Column, param.Flush, autoPlay, now); err != nil { |
|
log.Error("%+v", err) |
|
} |
|
if noCache { |
|
isRcmd = true |
|
return |
|
} |
|
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) indexAd2(c context.Context, plat int8, buvid string, mid int64, param *feed.IndexParam, zone *locmdl.Info, style int, now time.Time) (adm map[int]*cm.AdInfo, adAidm map[int64]struct{}, err error) { |
|
var advert *cm.Ad |
|
resource := s.adResource(plat, param.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, param.Build, buvid, []int64{resource}, country, province, city, param.Network, param.MobiApp, param.Device, param.OpenEvent, param.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) indexBanner2(c context.Context, plat int8, buvid string, mid int64, param *feed.IndexParam) (banners []*banner.Banner, version string, err error) { |
|
hash := param.BannerHash |
|
if param.LoginEvent != 0 { |
|
hash = "" |
|
} |
|
banners, version, err = s.banners(c, plat, param.Build, mid, buvid, param.Network, param.MobiApp, param.Device, param.OpenEvent, param.AdExtra, hash) |
|
return |
|
} |
|
|
|
func (s *Service) mergeItem2(c context.Context, plat int8, mid int64, rs []*ai.Item, adm map[int]*cm.AdInfo, adAidm map[int64]struct{}, banners []*banner.Banner, version string, blackAids map[int64]struct{}, follow *operate.Card, followMode bool) (is []*ai.Item, adInfom map[int]*cm.AdInfo) { |
|
if len(rs) == 0 { |
|
return |
|
} |
|
const ( |
|
cardIndex = 7 |
|
cardIndexIPad = 17 |
|
cardOffset = 2 |
|
) |
|
if len(banners) != 0 { |
|
rs = append([]*ai.Item{&ai.Item{Goto: model.GotoBanner, Banners: banners, Version: version}}, rs...) |
|
for index, ad := range adm { |
|
if _, ok := _cardAdWebm[ad.CardType]; ok && ((model.IsIPad(plat) && index <= cardIndexIPad) || index <= cardIndex) { |
|
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 existsAdWeb bool |
|
for _, r := range rs { |
|
for { |
|
if ad, ok := adm[len(is)]; ok { |
|
if ad.CreativeID != 0 { |
|
var item *ai.Item |
|
if _, ok := _cardAdAvm[ad.CardType]; ok { |
|
item = &ai.Item{ID: ad.CreativeContent.VideoID, Goto: model.GotoAdAv, Ad: ad} |
|
} else if _, ok := _cardAdWebm[ad.CardType]; ok { |
|
item = &ai.Item{Goto: model.GotoAdWeb, Ad: ad} |
|
existsAdWeb = true |
|
} else if _, ok := _cardAdWebSm[ad.CardType]; ok { |
|
item = &ai.Item{Goto: model.GotoAdWebS, Ad: ad} |
|
} else { |
|
b, _ := json.Marshal(ad) |
|
log.Error("ad---%s", b) |
|
break |
|
} |
|
is = append(is, item) |
|
continue |
|
} else { |
|
adInfom[ad.CardIndex-1] = 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 && len(is) != 0 { |
|
// banner 必须在第一位 |
|
continue |
|
} else if r.Goto == model.GotoRank && existsAdWeb { |
|
continue |
|
} else if r.Goto == model.GotoLogin && mid > 0 { |
|
continue |
|
} else if r.Goto == model.GotoFollowMode && !followMode { |
|
continue |
|
} |
|
is = append(is, r) |
|
} |
|
return |
|
} |
|
|
|
func (*Service) dealAdLoc(is []card.Handler, param *feed.IndexParam, adInfom map[int]*cm.AdInfo, now time.Time) { |
|
il := len(is) |
|
if il == 0 { |
|
return |
|
} |
|
if param.Idx < 1 { |
|
param.Idx = now.Unix() |
|
} |
|
for i, h := range is { |
|
if param.Pull { |
|
h.Get().Idx = param.Idx + int64(il-i) |
|
} else { |
|
h.Get().Idx = param.Idx - int64(i+1) |
|
} |
|
if ad, ok := adInfom[i]; ok { |
|
h.Get().AdInfo = ad |
|
} else if h.Get().AdInfo != nil { |
|
h.Get().AdInfo.CardIndex = i + 1 |
|
} |
|
} |
|
} |
|
|
|
func (s *Service) dealItem2(c context.Context, mid int64, buvid string, plat int8, rs []*ai.Item, param *feed.IndexParam, isRcmd, noCache, followMode bool, follow *operate.Card, now time.Time) (is []card.Handler, isAI bool) { |
|
if len(rs) == 0 { |
|
is = []card.Handler{} |
|
return |
|
} |
|
var ( |
|
aids, tids, roomIDs, sids, metaIDs, shopIDs, audioIDs, picIDs []int64 |
|
seasonIDs []int32 |
|
upIDs, avUpIDs, rmUpIDs, mtUpIDs []int64 |
|
am map[int64]*archive.ArchiveWithPlayer |
|
tagm map[int64]*tag.Tag |
|
rm map[int64]*live.Room |
|
sm map[int64]*bangumi.Season |
|
hasUpdate, getBanner bool |
|
update *bangumi.Update |
|
metam map[int64]*article.Meta |
|
shopm map[int64]*show.Shopping |
|
audiom map[int64]*audio.Audio |
|
cardm map[int64]*account.Card |
|
statm map[int64]*relation.Stat |
|
moe *bangumi.Moe |
|
isAtten map[int64]int8 |
|
arcOK bool |
|
rank *operate.Card |
|
seasonm map[int32]*episodegrpc.EpisodeCardsProto |
|
banners []*banner.Banner |
|
version string |
|
picm map[int64]*bplus.Picture |
|
) |
|
convergem := map[int64]*operate.Card{} |
|
followm := map[int64]*operate.Card{} |
|
downloadm := map[int64]*operate.Card{} |
|
specialm := map[int64]*operate.Card{} |
|
liveUpm := map[int64][]*live.Card{} |
|
isAI = isRcmd |
|
for _, r := range rs { |
|
if r == nil { |
|
continue |
|
} |
|
switch r.Goto { |
|
case model.GotoBanner: |
|
if len(r.Banners) != 0 { |
|
banners = r.Banners |
|
version = r.Version |
|
} else { |
|
getBanner = true |
|
} |
|
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: |
|
os, aid := s.RankCard(plat) |
|
rank = &operate.Card{} |
|
rank.FromRank(os) |
|
aids = append(aids, aid...) |
|
case model.GotoBangumiRcmd: |
|
hasUpdate = true |
|
case model.GotoConverge: |
|
cardm, aid, roomID, metaID := s.convergeCard(c, 3, r.ID) |
|
for id, card := range cardm { |
|
convergem[id] = card |
|
} |
|
aids = append(aids, aid...) |
|
roomIDs = append(roomIDs, roomID...) |
|
metaIDs = append(metaIDs, metaID...) |
|
case model.GotoGameDownloadS: |
|
cardm := s.downloadCard(c, r.ID) |
|
for id, card := range cardm { |
|
downloadm[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: |
|
cardm, upID := s.liveUpRcmdCard(c, r.ID) |
|
for id, card := range cardm { |
|
liveUpm[id] = card |
|
} |
|
upIDs = append(upIDs, upID...) |
|
case model.GotoSubscribe: |
|
cardm, upID, tid := s.subscribeCard(c, r.ID) |
|
for id, card := range cardm { |
|
followm[id] = card |
|
} |
|
upIDs = append(upIDs, upID...) |
|
tids = append(tids, tid...) |
|
case model.GotoSearchSubscribe: |
|
if follow != nil { |
|
followm[follow.ID] = follow |
|
for _, item := range follow.Items { |
|
upIDs = append(upIDs, item.ID) |
|
} |
|
} |
|
case model.GotoChannelRcmd: |
|
cardm, aid, tid := s.channelRcmdCard(c, r.ID) |
|
for id, card := range cardm { |
|
followm[id] = card |
|
} |
|
aids = append(aids, aid...) |
|
tids = append(tids, tid...) |
|
case model.GotoSpecial, model.GotoSpecialS: |
|
cardm := s.specialCard(c, r.ID) |
|
for id, card := range cardm { |
|
specialm[id] = card |
|
} |
|
case model.GotoPicture: |
|
if r.ID != 0 { |
|
picIDs = append(picIDs, r.ID) |
|
} |
|
if r.RcmdReason != nil && r.RcmdReason.Style == 4 { |
|
upIDs = append(upIDs, r.RcmdReason.FollowedMid) |
|
} |
|
} |
|
} |
|
g, ctx := errgroup.WithContext(c) |
|
if getBanner { |
|
g.Go(func() (err error) { |
|
if banners, version, err = s.banners(ctx, plat, param.Build, mid, buvid, param.Network, param.MobiApp, param.Device, param.OpenEvent, param.AdExtra, ""); err != nil { |
|
log.Error("%+v", err) |
|
err = nil |
|
} |
|
return |
|
}) |
|
} |
|
if len(aids) != 0 { |
|
g.Go(func() (err error) { |
|
if am, err = s.ArchivesWithPlayer(ctx, aids, param.Qn, param.MobiApp, param.Fnver, param.Fnval, param.ForceHost, param.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 seasonm, err = s.bgm.CardsInfoReply(ctx, seasonIDs); err != nil { |
|
log.Error("%+v", err) |
|
err = nil |
|
} |
|
return |
|
}) |
|
} |
|
if hasUpdate && mid > 0 { |
|
g.Go(func() (err error) { |
|
/* |
|
{ |
|
"code": 0, |
|
"message": "success", |
|
"result": { |
|
"title": "小埋。。。", |
|
"square_cover": "http://i0.hdslb.com/bfs/bangumi/dd2281c9f1c44e07c835e488ce1e1bae36f533e3.jpg", |
|
"updates": 67 |
|
} |
|
} |
|
*/ |
|
if update, err = s.bgm.Updates(ctx, mid, now); err != nil { |
|
log.Error("%+v", err) |
|
err = nil |
|
} |
|
return |
|
}) |
|
} |
|
if len(metaIDs) != 0 { |
|
g.Go(func() (err error) { |
|
if metam, err = s.art.Articles(ctx, metaIDs); err != nil { |
|
log.Error("%+v", err) |
|
err = nil |
|
} |
|
for _, meta := range metam { |
|
if meta.Author != nil { |
|
mtUpIDs = append(mtUpIDs, meta.Author.Mid) |
|
} |
|
} |
|
return |
|
}) |
|
} |
|
if len(shopIDs) != 0 { |
|
g.Go(func() (err error) { |
|
if shopm, 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 audiom, err = s.audio.Audios(ctx, audioIDs); err != nil { |
|
log.Error("%+v", err) |
|
err = nil |
|
} |
|
return |
|
}) |
|
} |
|
if len(picIDs) != 0 { |
|
g.Go(func() (err error) { |
|
if picm, err = s.bplus.DynamicDetail(ctx, picIDs...); err != nil { |
|
log.Error("%+v", err) |
|
err = nil |
|
} |
|
return |
|
}) |
|
} |
|
// 萌战下线 |
|
// if mid > 0 { |
|
// g.Go(func() (err error) { |
|
// if moe, err = s.bgm.FollowPull(ctx, mid, mobiApp, device, now); err != nil { |
|
// log.Error("%+v", err) |
|
// err = nil |
|
// } |
|
// return |
|
// }) |
|
// } |
|
if err := g.Wait(); err != nil { |
|
log.Error("%+v", err) |
|
if noCache { |
|
is = []card.Handler{} |
|
return |
|
} |
|
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 cardm, err = s.acc.Cards3(ctx, upIDs); err != nil { |
|
log.Error("%+v", err) |
|
err = nil |
|
} |
|
return |
|
}) |
|
g.Go(func() (err error) { |
|
if statm, err = s.rel.Stats(ctx, upIDs); err != nil { |
|
log.Error("%+v", err) |
|
err = nil |
|
} |
|
return |
|
}) |
|
if mid > 0 && param.RecsysMode == 0 { |
|
g.Go(func() error { |
|
isAtten = s.acc.IsAttention(ctx, upIDs, mid) |
|
return nil |
|
}) |
|
} |
|
} |
|
g.Wait() |
|
} |
|
isAI = isAI && arcOK |
|
if moe != nil { |
|
moePos := s.c.Feed.Index.MoePosition |
|
if moePos-1 >= 0 && moePos-1 <= len(rs) { |
|
rs = append(rs[:moePos-1], append([]*ai.Item{&ai.Item{ID: moe.ID, Goto: model.GotoMoe}}, rs[moePos-1:]...)...) |
|
} |
|
} |
|
var cardTotal int |
|
is = make([]card.Handler, 0, len(rs)) |
|
insert := map[int]card.Handler{} |
|
for _, r := range rs { |
|
if r == nil { |
|
continue |
|
} |
|
var ( |
|
main interface{} |
|
cardType cdm.CardType |
|
) |
|
op := &operate.Card{} |
|
op.From(cdm.CardGt(r.Goto), r.ID, r.Tid, plat, param.Build) |
|
// 卡片展示点赞数实验 |
|
// if mid%20 == 11 && ((plat == model.PlatIPhone && param.Build >= 8290) || (plat == model.PlatAndroid && param.Build >= 5360000)) { |
|
// op.FromSwitch(cdm.SwitchFeedIndexLike) |
|
// } |
|
// 变化卡片类型 |
|
switch r.Goto { |
|
case model.GotoSpecialS, model.GotoGameDownloadS, model.GotoShoppingS: |
|
if r.Style == 2 { |
|
cardType = cdm.LargeCoverV1 |
|
} |
|
case model.GotoPicture: |
|
if p, ok := picm[r.ID]; ok { |
|
switch cdm.Columnm[param.Column] { |
|
case cdm.ColumnSvrSingle: |
|
if len(p.Imgs) < 3 { |
|
cardType = cdm.OnePicV1 |
|
} else { |
|
cardType = cdm.ThreePicV1 |
|
} |
|
case cdm.ColumnSvrDouble: |
|
if len(p.Imgs) < 3 { |
|
// 版本过滤5.37为新卡片 |
|
if (plat == model.PlatIPhone && param.Build > 8300) || (plat == model.PlatAndroid && param.Build > 5365000) { |
|
cardType = cdm.OnePicV2 |
|
} else { |
|
cardType = cdm.SmallCoverV2 |
|
} |
|
} else { |
|
cardType = cdm.ThreePicV2 |
|
} |
|
default: |
|
continue |
|
} |
|
} else { |
|
continue |
|
} |
|
case model.GotoInterest: |
|
switch cdm.Columnm[param.Column] { |
|
case cdm.ColumnSvrSingle: |
|
cardType = cdm.OptionsV1 |
|
case cdm.ColumnSvrDouble: |
|
cardType = cdm.OptionsV2 |
|
default: |
|
continue |
|
} |
|
case model.GotoFollowMode: |
|
cardType = cdm.Select |
|
default: |
|
} |
|
h := card.Handle(plat, cdm.CardGt(r.Goto), cardType, param.Column, r, tagm, isAtten, statm, cardm) |
|
if h == nil { |
|
continue |
|
} |
|
switch r.Goto { |
|
case model.GotoAv, model.GotoUpRcmdAv, model.GotoPlayer: |
|
if !arcOK { |
|
if r.Archive != nil { |
|
am = map[int64]*archive.ArchiveWithPlayer{r.Archive.Aid: &archive.ArchiveWithPlayer{Archive3: r.Archive}} |
|
} |
|
if r.Tag != nil { |
|
tagm = map[int64]*tag.Tag{r.Tag.ID: r.Tag} |
|
op.Tid = r.Tag.ID |
|
} |
|
} |
|
if a, ok := am[r.ID]; ok && (a.AttrVal(archive.AttrBitOverseaLock) == 0 || !model.IsOverseas(plat)) { |
|
main = am |
|
op.TrackID = r.TrackID |
|
} |
|
if plat == model.PlatIPhone && param.Build > 8290 || plat == model.PlatAndroid && param.Build > 5365000 { |
|
op.Switch = cdm.SwitchCooperationShow |
|
} else { |
|
op.Switch = cdm.SwitchCooperationHide |
|
} |
|
case model.GotoLive, model.GotoPlayerLive: |
|
main = rm |
|
case model.GotoBangumi: |
|
main = sm |
|
case model.GotoPGC: |
|
main = seasonm |
|
case model.GotoLogin: |
|
op.FromLogin(r.ID) |
|
case model.GotoSpecial, model.GotoSpecialS: |
|
op = specialm[r.ID] |
|
case model.GotoRank: |
|
main = map[cdm.Gt]interface{}{cdm.GotoAv: am} |
|
op = rank |
|
case model.GotoBangumiRcmd: |
|
main = update |
|
case model.GotoBanner: |
|
op.FromBanner(banners, version) |
|
case model.GotoConverge: |
|
main = map[cdm.Gt]interface{}{cdm.GotoAv: am, cdm.GotoLive: rm, cdm.GotoArticle: metam} |
|
op = convergem[r.ID] |
|
case model.GotoGameDownloadS: |
|
op = downloadm[r.ID] |
|
case model.GotoArticleS: |
|
main = metam |
|
case model.GotoShoppingS: |
|
main = shopm |
|
case model.GotoAudio: |
|
main = audiom |
|
case model.GotoChannelRcmd: |
|
main = am |
|
op = followm[r.ID] |
|
case model.GotoSubscribe, model.GotoSearchSubscribe: |
|
op = followm[r.ID] |
|
case model.GotoLiveUpRcmd: |
|
main = liveUpm |
|
case model.GotoMoe: |
|
main = moe |
|
case model.GotoPicture: |
|
main = picm |
|
case model.GotoAdAv: |
|
main = am |
|
op.FromAdAv(r.Ad) |
|
case model.GotoAdWebS, model.GotoAdWeb: |
|
main = r.Ad |
|
case model.GotoInterest: |
|
main = s.c.Feed.Index.Interest |
|
case model.GotoFollowMode: |
|
var ( |
|
title string |
|
desc string |
|
button []string |
|
) |
|
if s.c.Feed.Index.FollowMode != nil && s.c.Feed.Index.FollowMode.Card != nil { |
|
title = s.c.Feed.Index.FollowMode.Card.Title |
|
desc = s.c.Feed.Index.FollowMode.Card.Desc |
|
button = s.c.Feed.Index.FollowMode.Card.Button |
|
} |
|
op.FromFollowMode(title, desc, button) |
|
default: |
|
log.Warn("unexpected goto(%s) %+v", r.Goto, r) |
|
continue |
|
} |
|
if op != nil { |
|
op.Plat = plat |
|
op.Build = param.Build |
|
} |
|
h.From(main, op) |
|
// 卡片不正常要continue |
|
if !h.Get().Right { |
|
continue |
|
} |
|
switch r.Goto { |
|
case model.GotoAdAv, model.GotoAdWebS, model.GotoAdWeb: |
|
// 判断结果列表长度,如果列表的末尾不是广告位,则放到插入队列里 |
|
if len(is) != r.Ad.CardIndex-1 { |
|
insert[r.Ad.CardIndex-1] = h |
|
// 插入队列后一定要continue,否则就直接加到队列末尾了 |
|
continue |
|
} |
|
} |
|
is, cardTotal = s.appendItem(plat, is, h, param.Column, cardTotal) |
|
// 从插入队列里获取广告 |
|
if h, ok := insert[len(is)]; ok { |
|
is, cardTotal = s.appendItem(plat, is, h, param.Column, cardTotal) |
|
} |
|
} |
|
// 双列末尾卡片去空窗 |
|
if !model.IsIPad(plat) { |
|
if cdm.Columnm[param.Column] == cdm.ColumnSvrDouble { |
|
is = is[:len(is)-cardTotal%2] |
|
} |
|
} else { |
|
// 复杂的ipad去空窗逻辑 |
|
if cardTotal%4 == 3 { |
|
if is[len(is)-2].Get().CardLen == 2 { |
|
is = is[:len(is)-2] |
|
} else { |
|
is = is[:len(is)-3] |
|
} |
|
} else if cardTotal%4 == 2 { |
|
if is[len(is)-1].Get().CardLen == 2 { |
|
is = is[:len(is)-1] |
|
} else { |
|
is = is[:len(is)-2] |
|
} |
|
} else if cardTotal%4 == 1 { |
|
is = is[:len(is)-1] |
|
} |
|
} |
|
if len(is) == 0 { |
|
is = []card.Handler{} |
|
return |
|
} |
|
return |
|
} |
|
|
|
func (s *Service) appendItem(plat int8, rs []card.Handler, h card.Handler, column cdm.ColumnStatus, cardTotal int) (is []card.Handler, total int) { |
|
h.Get().ThreePointFrom() |
|
if !model.IsIPad(plat) { |
|
// 双列大小卡换位去空窗 |
|
if cdm.Columnm[column] == cdm.ColumnSvrDouble { |
|
// 通栏卡 |
|
if h.Get().CardLen == 0 { |
|
if cardTotal%2 == 1 { |
|
is = card.SwapTwoItem(rs, h) |
|
} else { |
|
is = append(rs, h) |
|
} |
|
} else { |
|
is = append(rs, h) |
|
} |
|
} else { |
|
is = append(rs, h) |
|
} |
|
} else { |
|
// ipad卡片不展示标签 |
|
h.Get().DescButton = nil |
|
// ipad大小卡换位去空窗 |
|
if h.Get().CardLen == 0 { |
|
// 通栏卡 |
|
if cardTotal%4 == 3 { |
|
is = card.SwapFourItem(rs, h) |
|
} else if cardTotal%4 == 2 { |
|
is = card.SwapThreeItem(rs, h) |
|
} else if cardTotal%4 == 1 { |
|
is = card.SwapTwoItem(rs, h) |
|
} else { |
|
is = append(rs, h) |
|
} |
|
} else if h.Get().CardLen == 2 { |
|
// 半栏卡 |
|
if cardTotal%4 == 3 { |
|
is = card.SwapTwoItem(rs, h) |
|
} else if cardTotal%4 == 2 { |
|
is = append(rs, h) |
|
} else if cardTotal%4 == 1 { |
|
is = card.SwapTwoItem(rs, h) |
|
} else { |
|
is = append(rs, h) |
|
} |
|
} else { |
|
is = append(rs, h) |
|
} |
|
} |
|
total = cardTotal + h.Get().CardLen |
|
return |
|
} |
|
|
|
func (s *Service) Converge(c context.Context, mid int64, plat int8, param *feed.ConvergeParam, now time.Time) (is []card.Handler, converge *operate.Card, err error) { |
|
cardm, _, _, _ := s.convergeCard(c, 0, param.ID) |
|
converge, ok := cardm[param.ID] |
|
if !ok { |
|
is = []card.Handler{} |
|
return |
|
} |
|
rs := make([]*ai.Item, 0, len(converge.Items)) |
|
for _, item := range converge.Items { |
|
rs = append(rs, &ai.Item{ID: item.ID, Goto: string(item.CardGoto)}) |
|
} |
|
indexParam := &feed.IndexParam{ |
|
MobiApp: param.MobiApp, |
|
Device: param.Device, |
|
Build: param.Build, |
|
Qn: param.Qn, |
|
Fnver: param.Fnver, |
|
Fnval: param.Fnval, |
|
ForceHost: param.ForceHost, |
|
} |
|
is, _ = s.dealItem2(c, mid, "", plat, rs, indexParam, false, true, false, nil, now) |
|
for _, item := range is { |
|
// 运营tab页没有不感兴趣 |
|
item.Get().ThreePointWatchLater() |
|
} |
|
return |
|
}
|
|
|