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.
438 lines
12 KiB
438 lines
12 KiB
package feed |
|
|
|
import ( |
|
"context" |
|
"encoding/json" |
|
"time" |
|
|
|
cdm "go-common/app/interface/main/app-card/model" |
|
"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/banner" |
|
"go-common/app/interface/main/app-card/model/card/cm" |
|
"go-common/app/interface/main/app-card/model/card/operate" |
|
"go-common/app/interface/main/app-intl/model" |
|
"go-common/app/interface/main/app-intl/model/feed" |
|
tag "go-common/app/interface/main/tag/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" |
|
"go-common/library/ecode" |
|
"go-common/library/log" |
|
"go-common/library/net/metadata" |
|
"go-common/library/sync/errgroup" |
|
"go-common/library/text/translate/chinese" |
|
) |
|
|
|
// Index is. |
|
func (s *Service) Index(c context.Context, buvid string, mid int64, plat int8, param *feed.IndexParam, now time.Time, style int) (is []card.Handler, userFeature json.RawMessage, isRcmd, newUser bool, code int, autoPlay, clean int8, autoPlayInfoc string, 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 |
|
ip = metadata.String(c, metadata.RemoteIP) |
|
info *locmdl.Info |
|
isTW = model.TWLocale(param.Locale) |
|
) |
|
// 国际版不做abtest |
|
clean = 0 |
|
autoPlay = 2 |
|
group := s.group(mid, buvid) |
|
if info, err = s.loc.Info(c, ip); err != nil { |
|
log.Warn("s.loc.Info(%v) error(%v)", ip, err) |
|
err = nil |
|
} |
|
if !s.c.Feed.Index.Abnormal { |
|
g, ctx := errgroup.WithContext(c) |
|
g.Go(func() error { |
|
rs, userFeature, isRcmd, newUser, code = s.indexRcmd(ctx, plat, param.Build, buvid, mid, group, param.LoginEvent, info, param.Interest, param.Network, style, param.Column, param.Flush, autoPlayInfoc, now) |
|
if isTW { |
|
for _, r := range rs { |
|
if r.RcmdReason != nil { |
|
r.RcmdReason.Content = chinese.Convert(ctx, r.RcmdReason.Content) |
|
} |
|
} |
|
} |
|
return nil |
|
}) |
|
g.Go(func() (err error) { |
|
if blackAidm, err = s.BlackList(ctx, mid); err != nil { |
|
log.Error("%+v", err) |
|
err = nil |
|
} |
|
return |
|
}) |
|
if err = g.Wait(); err != nil { |
|
return |
|
} |
|
rs, adInfom = s.mergeItem(c, mid, rs, adm, adAidm, banners, version, blackAidm, plat, follow) |
|
} else { |
|
count := s.indexCount(plat) |
|
rs = s.recommendCache(count) |
|
log.Warn("feed index show disaster recovery data len(%d)", len(is)) |
|
} |
|
is, isRcmd, err = s.dealItem(c, param.Column, mid, buvid, plat, param.Build, rs, isRcmd, param.MobiApp, param.Device, param.Network, param.OpenEvent, param.AdExtra, param.Qn, param.Fnver, param.Fnval, follow, isTW, now) |
|
s.dealAdLoc(is, param, adInfom, now) |
|
return |
|
} |
|
|
|
// indexRcmd is. |
|
func (s *Service) indexRcmd(c context.Context, plat int8, build int, buvid string, mid int64, group int, loginEvent int, info *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 info != nil { |
|
zoneID = info.ZoneID |
|
} |
|
if is, userFeature, code, newUser, err = s.rcmd.Recommend(c, plat, buvid, mid, build, loginEvent, 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 |
|
} |
|
|
|
// mergeItem is. |
|
func (s *Service) mergeItem(c context.Context, mid int64, rs []*ai.Item, adm map[int]*cm.AdInfo, adAidm map[int64]struct{}, banners []*banner.Banner, version string, blackAids map[int64]struct{}, plat int8, follow *operate.Card) (is []*ai.Item, adInfom map[int]*cm.AdInfo) { |
|
if len(rs) == 0 { |
|
return |
|
} |
|
if len(banners) != 0 { |
|
rs = append([]*ai.Item{{Goto: model.GotoBanner, Banners: banners, Version: version}}, rs...) |
|
} |
|
is = make([]*ai.Item, 0, len(rs)+len(adm)) |
|
adInfom = make(map[int]*cm.AdInfo, len(adm)) |
|
for _, r := range rs { |
|
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.GotoLogin && mid != 0 { |
|
continue |
|
} |
|
is = append(is, r) |
|
} |
|
return |
|
} |
|
|
|
// dealAdLoc is. |
|
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 |
|
} |
|
} |
|
} |
|
|
|
// dealItem is. |
|
func (s *Service) dealItem(c context.Context, column cdm.ColumnStatus, mid int64, buvid string, plat int8, build int, rs []*ai.Item, isRcmd bool, mobiApp, device, network, openEvent, adExtra string, qn, fnver, fnval int, follow *operate.Card, isTW bool, now time.Time) (is []card.Handler, isAI bool, err error) { |
|
if len(rs) == 0 { |
|
is = []card.Handler{} |
|
return |
|
} |
|
var ( |
|
aids, tids []int64 |
|
upIDs, avUpIDs, rmUpIDs, mtUpIDs []int64 |
|
am map[int64]*archive.ArchiveWithPlayer |
|
tagm map[int64]*tag.Tag |
|
rank *operate.Card |
|
cardm map[int64]*account.Card |
|
statm map[int64]*relation.Stat |
|
isAtten map[int64]int8 |
|
arcOK bool |
|
) |
|
followm := map[int64]*operate.Card{} |
|
isAI = isRcmd |
|
for _, r := range rs { |
|
if r == nil { |
|
continue |
|
} |
|
if isTW && r.RcmdReason != nil { |
|
r.RcmdReason.Content = chinese.Convert(c, r.RcmdReason.Content) |
|
} |
|
switch r.Goto { |
|
case model.GotoAv, model.GotoPlayer, model.GotoUpRcmdAv: |
|
if r.ID != 0 { |
|
aids = append(aids, r.ID) |
|
} |
|
if r.Tid != 0 { |
|
tids = append(tids, r.Tid) |
|
} |
|
case model.GotoRank: |
|
os, aid := s.RankCard(plat) |
|
rank = &operate.Card{} |
|
rank.FromRank(os) |
|
aids = append(aids, aid...) |
|
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...) |
|
} |
|
} |
|
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); err != nil { |
|
return |
|
} |
|
arcOK = true |
|
for _, a := range am { |
|
avUpIDs = append(avUpIDs, a.Author.Mid) |
|
if isTW { |
|
out := chinese.Converts(ctx, a.Title, a.Desc, a.TypeName) |
|
a.Title = out[a.Title] |
|
a.Desc = out[a.Desc] |
|
a.TypeName = out[a.TypeName] |
|
} |
|
} |
|
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 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 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 { |
|
g.Go(func() error { |
|
isAtten = s.acc.IsAttention(ctx, upIDs, mid) |
|
return nil |
|
}) |
|
} |
|
} |
|
g.Wait() |
|
} |
|
isAI = isAI && arcOK |
|
var cardTotal int |
|
is = make([]card.Handler, 0, len(rs)) |
|
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, build) |
|
h := card.Handle(plat, cdm.CardGt(r.Goto), cardType, column, r, tagm, isAtten, statm, cardm) |
|
if h == nil { |
|
continue |
|
} |
|
switch r.Goto { |
|
case model.GotoAv, model.GotoPlayer, model.GotoUpRcmdAv: |
|
if !arcOK { |
|
if r.Archive != nil { |
|
if isTW { |
|
out := chinese.Converts(c, r.Archive.Title, r.Archive.Desc, r.Archive.TypeName, r.Archive.Author.Name) |
|
r.Archive.Title = out[r.Archive.Title] |
|
r.Archive.Desc = out[r.Archive.Desc] |
|
r.Archive.TypeName = out[r.Archive.TypeName] |
|
} |
|
am = map[int64]*archive.ArchiveWithPlayer{r.Archive.Aid: {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 |
|
} |
|
case model.GotoRank: |
|
main = map[cdm.Gt]interface{}{cdm.GotoAv: am} |
|
op = rank |
|
case model.GotoChannelRcmd: |
|
main = am |
|
case model.GotoLogin: |
|
op.FromLogin(r.ID) |
|
default: |
|
log.Warn("unexpected goto(%s) %+v", r.Goto, r) |
|
continue |
|
} |
|
h.From(main, op) |
|
// 卡片不正常要continue |
|
if !h.Get().Right { |
|
continue |
|
} |
|
is, cardTotal = s.appendItem(plat, is, h, column, cardTotal) |
|
} |
|
// 双列末尾卡片去空窗 |
|
if !model.IsIPad(plat) { |
|
if cdm.Columnm[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 |
|
} |
|
|
|
// appendItem is. |
|
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 h.Get().ThreePoint != nil { |
|
h.Get().ThreePoint.Feedbacks = nil |
|
} |
|
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 |
|
} |
|
|
|
// indexCount is. |
|
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 |
|
}
|
|
|