600 lines
16 KiB
Go
600 lines
16 KiB
Go
package channel
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
|
|
cdm "go-common/app/interface/main/app-card/model"
|
|
cardm "go-common/app/interface/main/app-card/model/card"
|
|
"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/live"
|
|
"go-common/app/interface/main/app-card/model/card/operate"
|
|
shopping "go-common/app/interface/main/app-card/model/card/show"
|
|
"go-common/app/interface/main/app-channel/model"
|
|
"go-common/app/interface/main/app-channel/model/card"
|
|
"go-common/app/interface/main/app-channel/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"
|
|
relation "go-common/app/service/main/relation/model"
|
|
episodegrpc "go-common/app/service/openplatform/pgc-season/api/grpc/episode/v1"
|
|
seasongrpc "go-common/app/service/openplatform/pgc-season/api/grpc/season/v1"
|
|
"go-common/library/log"
|
|
"go-common/library/sync/errgroup"
|
|
|
|
farm "github.com/dgryski/go-farm"
|
|
)
|
|
|
|
const (
|
|
_fTypeOperation = "operation"
|
|
_fTypeRecommend = "recommend"
|
|
)
|
|
|
|
// Index channel index
|
|
func (s *Service) Index2(c context.Context, mid, channelID, idx int64, plat int8, mobiApp, device, buvid, channelName, ip string,
|
|
build, loginEvent, displayID, qn, fnver, fnval int, pull bool, now time.Time) (res *feed.Show2, err error) {
|
|
var (
|
|
aiCards []*card.Card
|
|
requestCnt = 10
|
|
isIpad = plat == model.PlatIPad
|
|
topic cardm.Handler
|
|
item []cardm.Handler
|
|
channelResource *tag.ChannelResource
|
|
topChannel, isRec int
|
|
infocs []*feed.Item
|
|
)
|
|
if isIpad {
|
|
requestCnt = 20
|
|
}
|
|
if channelID > 0 {
|
|
channelName = ""
|
|
}
|
|
g, ctx := errgroup.WithContext(c)
|
|
g.Go(func() (err error) {
|
|
if channelResource, err = s.tg.Resources(ctx, plat, channelID, mid, channelName, buvid, build, requestCnt, loginEvent, displayID); err != nil {
|
|
log.Error("index s.tg.Resources error(%v)", err)
|
|
return
|
|
}
|
|
if channelResource != nil {
|
|
aids := channelResource.Oids
|
|
for _, aid := range aids {
|
|
t := &card.Card{
|
|
Type: model.GotoAv,
|
|
Value: aid,
|
|
FromType: _fTypeRecommend,
|
|
}
|
|
aiCards = append(aiCards, t)
|
|
}
|
|
if channelResource.Failover {
|
|
isRec = 0
|
|
} else {
|
|
isRec = 1
|
|
}
|
|
if channelResource.IsChannel {
|
|
topChannel = 1
|
|
} else {
|
|
topChannel = 0
|
|
}
|
|
}
|
|
return
|
|
})
|
|
g.Go(func() (err error) {
|
|
var t *tag.ChannelDetail
|
|
if t, err = s.tg.ChannelDetail(c, mid, channelID, channelName, s.isOverseas(plat)); err != nil {
|
|
log.Error("s.tag.ChannelDetail(%d, %d, %s) error(%v)", mid, channelID, channelName, err)
|
|
return
|
|
}
|
|
channelID = t.Tag.ID
|
|
channelName = t.Tag.Name
|
|
return
|
|
})
|
|
err = g.Wait()
|
|
//infoc
|
|
infoc := &feedInfoc{
|
|
mobiApp: mobiApp,
|
|
device: device,
|
|
build: strconv.Itoa(build),
|
|
now: now.Format("2006-01-02 15:04:05"),
|
|
pull: strconv.FormatBool(pull),
|
|
loginEvent: strconv.Itoa(loginEvent),
|
|
channelID: strconv.FormatInt(channelID, 10),
|
|
channelName: channelName,
|
|
mid: strconv.FormatInt(mid, 10),
|
|
buvid: buvid,
|
|
displayID: strconv.Itoa(displayID),
|
|
isRec: strconv.Itoa(isRec),
|
|
topChannel: strconv.Itoa(topChannel),
|
|
ServerCode: "0",
|
|
}
|
|
//infoc
|
|
if err != nil {
|
|
log.Error("RankUser errgroup.WithContext error(%v)", err)
|
|
res = &feed.Show2{
|
|
Feed: []cardm.Handler{},
|
|
}
|
|
infoc.Items = []*feed.Item{}
|
|
infoc.ServerCode = err.Error()
|
|
s.infoc(infoc)
|
|
return
|
|
}
|
|
var (
|
|
tmps = []*card.Card{}
|
|
)
|
|
if loginEvent == 1 || loginEvent == 2 {
|
|
if cards, ok := s.cardCache[channelID]; ok {
|
|
isShowCard := s.isShowOperationCards(c, buvid, channelID, cards, now)
|
|
for _, c := range cards {
|
|
if !isShowCard && c.Type != model.GotoTopstick {
|
|
continue
|
|
}
|
|
t := &card.Card{}
|
|
*t = *c
|
|
t.FromType = _fTypeOperation
|
|
tmps = append(tmps, t)
|
|
}
|
|
tmps = append(tmps, aiCards...)
|
|
} else {
|
|
tmps = aiCards
|
|
}
|
|
} else {
|
|
tmps = aiCards
|
|
}
|
|
topic, item, infocs, err = s.dealItem2(c, mid, idx, channelID, plat, build, buvid, ip, mobiApp, pull, qn, fnver, fnval, now, tmps)
|
|
res = &feed.Show2{
|
|
Topic: topic,
|
|
Feed: item,
|
|
}
|
|
infoc.Items = infocs
|
|
s.infoc(infoc)
|
|
return
|
|
}
|
|
|
|
// dealItem
|
|
func (s *Service) dealItem2(c context.Context, mid, idx, channelID int64, plat int8, build int, buvid, ip, mobiApp string, pull bool,
|
|
qn, fnver, fnval int, now time.Time, cards []*card.Card) (top cardm.Handler, is []cardm.Handler, infocs []*feed.Item, err error) {
|
|
if len(cards) == 0 {
|
|
is = []cardm.Handler{}
|
|
return
|
|
}
|
|
var (
|
|
aids, shopIDs, audioIDs, sids, roomIDs, metaIDs []int64
|
|
upIDs, tids, rmUpIDs, mtUpIDs, avUpIDs, avUpCountIDs []int64
|
|
seasonIDs, epIDs []int32
|
|
am map[int64]*archive.ArchiveWithPlayer
|
|
tagm map[int64]*tag.Tag
|
|
rm map[int64]*live.Room
|
|
sm map[int64]*bangumi.Season
|
|
metam map[int64]*article.Meta
|
|
shopm map[int64]*shopping.Shopping
|
|
audiom map[int64]*audio.Audio
|
|
cardAids = map[int64]struct{}{}
|
|
ac map[int64]*account.Card
|
|
statm map[int64]*relation.Stat
|
|
isAtten map[int64]int8
|
|
upAvCount = map[int64]int{}
|
|
channelCards []*card.Card
|
|
seasonm map[int32]*seasongrpc.CardInfoProto
|
|
epidsCards map[int32]*episodegrpc.EpisodeCardsProto
|
|
// key
|
|
_initCardPlatKey = "card_platkey_%d_%d"
|
|
)
|
|
specialm := map[int64]*operate.Card{}
|
|
convergem := map[int64]*operate.Card{}
|
|
downloadm := map[int64]*operate.Card{}
|
|
followm := map[int64]*operate.Card{}
|
|
liveUpm := map[int64][]*live.Card{}
|
|
cardSet := map[int64]*operate.Card{}
|
|
LOOP:
|
|
for _, card := range cards {
|
|
key := fmt.Sprintf(_initCardPlatKey, plat, card.ID)
|
|
if cardPlat, ok := s.cardPlatCache[key]; ok {
|
|
for _, l := range cardPlat {
|
|
if model.InvalidBuild(build, l.Build, l.Condition) {
|
|
continue LOOP
|
|
}
|
|
}
|
|
} else if card.FromType == _fTypeOperation {
|
|
continue LOOP
|
|
}
|
|
channelCards = append(channelCards, card)
|
|
switch card.Type {
|
|
case model.GotoAv, model.GotoPlayer, model.GotoUpRcmdAv:
|
|
if card.Value != 0 {
|
|
aids = append(aids, card.Value)
|
|
cardAids[card.Value] = struct{}{}
|
|
}
|
|
case model.GotoLive, model.GotoPlayerLive:
|
|
if card.Value != 0 {
|
|
roomIDs = append(roomIDs, card.Value)
|
|
}
|
|
case model.GotoBangumi:
|
|
if card.Value != 0 {
|
|
sids = append(sids, card.Value)
|
|
}
|
|
case model.GotoPGC:
|
|
if card.Value != 0 {
|
|
epIDs = append(epIDs, int32(card.Value))
|
|
}
|
|
case model.GotoConverge:
|
|
if card.Value != 0 {
|
|
cardm, aid, roomID, metaID := s.convergeCard2(c, 3, card.Value)
|
|
for id, card := range cardm {
|
|
convergem[id] = card
|
|
}
|
|
aids = append(aids, aid...)
|
|
roomIDs = append(roomIDs, roomID...)
|
|
metaIDs = append(metaIDs, metaID...)
|
|
}
|
|
case model.GotoGameDownload, model.GotoGameDownloadS:
|
|
if card.Value != 0 {
|
|
cardm := s.downloadCard(c, card.Value)
|
|
for id, card := range cardm {
|
|
downloadm[id] = card
|
|
}
|
|
}
|
|
case model.GotoArticle, model.GotoArticleS:
|
|
if card.Value != 0 {
|
|
metaIDs = append(metaIDs, card.Value)
|
|
}
|
|
case model.GotoShoppingS:
|
|
if card.Value != 0 {
|
|
shopIDs = append(shopIDs, card.Value)
|
|
}
|
|
case model.GotoAudio:
|
|
if card.Value != 0 {
|
|
audioIDs = append(audioIDs, card.Value)
|
|
}
|
|
case model.GotoChannelRcmd:
|
|
cardm, aid, tid := s.channelRcmdCard(c, card.Value)
|
|
for id, card := range cardm {
|
|
followm[id] = card
|
|
}
|
|
aids = append(aids, aid...)
|
|
tids = append(tids, tid...)
|
|
case model.GotoLiveUpRcmd:
|
|
if card.Value != 0 {
|
|
cardm, upID := s.liveUpRcmdCard(c, card.Value)
|
|
for id, card := range cardm {
|
|
liveUpm[id] = card
|
|
}
|
|
upIDs = append(upIDs, upID...)
|
|
}
|
|
case model.GotoSubscribe:
|
|
if card.Value != 0 {
|
|
cardm, upID, tid := s.subscribeCard(c, card.Value)
|
|
for id, card := range cardm {
|
|
followm[id] = card
|
|
}
|
|
upIDs = append(upIDs, upID...)
|
|
tids = append(tids, tid...)
|
|
}
|
|
case model.GotoSpecial, model.GotoSpecialS:
|
|
cardm := s.specialCard(c, card.Value)
|
|
for id, card := range cardm {
|
|
specialm[id] = card
|
|
}
|
|
case model.GotoTopstick:
|
|
cardm := s.topstickCard(c, card.Value)
|
|
for id, card := range cardm {
|
|
specialm[id] = card
|
|
}
|
|
case model.GotoPgcsRcmd:
|
|
cardm, ssid := s.cardSetChange(c, card.Value)
|
|
seasonIDs = append(seasonIDs, ssid...)
|
|
for id, card := range cardm {
|
|
cardSet[id] = card
|
|
}
|
|
case model.GotoUpRcmdS:
|
|
if card.Value != 0 {
|
|
avUpCountIDs = append(avUpCountIDs, card.Value)
|
|
}
|
|
}
|
|
}
|
|
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, build); err != nil {
|
|
return
|
|
}
|
|
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 len(epIDs) != 0 {
|
|
g.Go(func() (err error) {
|
|
if epidsCards, err = s.bgm.EpidsCardsInfoReply(ctx, epIDs); 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.sp.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(avUpCountIDs) != 0 {
|
|
var mutex sync.Mutex
|
|
for _, upid := range avUpCountIDs {
|
|
var (
|
|
tmpupid = upid
|
|
)
|
|
g.Go(func() (err error) {
|
|
var cnt int
|
|
if cnt, err = s.arc.UpCount2(ctx, tmpupid); err != nil {
|
|
log.Error("%+v", err)
|
|
err = nil
|
|
}
|
|
mutex.Lock()
|
|
upAvCount[tmpupid] = cnt
|
|
mutex.Unlock()
|
|
return
|
|
})
|
|
}
|
|
}
|
|
if err = g.Wait(); err != nil {
|
|
log.Error("%+v", err)
|
|
return
|
|
}
|
|
upIDs = append(upIDs, avUpIDs...)
|
|
upIDs = append(upIDs, rmUpIDs...)
|
|
upIDs = append(upIDs, mtUpIDs...)
|
|
upIDs = append(upIDs, avUpCountIDs...)
|
|
g, ctx = errgroup.WithContext(c)
|
|
if len(upIDs) != 0 {
|
|
g.Go(func() (err error) {
|
|
if ac, 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
|
|
})
|
|
}
|
|
}
|
|
if err = g.Wait(); err != nil {
|
|
log.Error("%+v", err)
|
|
return
|
|
}
|
|
for _, card := range channelCards {
|
|
var (
|
|
r = card.CardToAiChange()
|
|
main interface{}
|
|
)
|
|
switch r.Goto {
|
|
case model.GotoAv, model.GotoUpRcmdAv, model.GotoPlayer:
|
|
r.HideButton = true
|
|
}
|
|
h := cardm.Handle(plat, cdm.CardGt(r.Goto), "", cdm.ColumnSvrSingle, r, tagm, isAtten, statm, ac)
|
|
if h == nil {
|
|
continue
|
|
}
|
|
op := &operate.Card{}
|
|
op.From(cdm.CardGt(r.Goto), r.ID, 0, plat, build)
|
|
switch r.Goto {
|
|
case model.GotoAv, model.GotoUpRcmdAv, model.GotoPlayer:
|
|
op.ShowUGCPay = true
|
|
if a, ok := am[r.ID]; ok && (a.AttrVal(archive.AttrBitOverseaLock) == 0 || !model.IsOverseas(plat)) {
|
|
main = am
|
|
}
|
|
op.Switch = cdm.SwitchCooperationHide
|
|
case model.GotoLive, model.GotoPlayerLive:
|
|
main = rm
|
|
case model.GotoBangumi:
|
|
main = sm
|
|
case model.GotoPGC:
|
|
main = epidsCards
|
|
case model.GotoSpecial, model.GotoSpecialS, model.GotoTopstick:
|
|
op = specialm[r.ID]
|
|
case model.GotoGameDownload, model.GotoGameDownloadS:
|
|
op = downloadm[r.ID]
|
|
case model.GotoArticle, 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:
|
|
op = followm[r.ID]
|
|
case model.GotoLiveUpRcmd:
|
|
main = liveUpm
|
|
case model.GotoConverge:
|
|
main = map[cdm.Gt]interface{}{cdm.GotoAv: am, cdm.GotoLive: rm, cdm.GotoArticle: metam}
|
|
op = convergem[r.ID]
|
|
case model.GotoPgcsRcmd:
|
|
main = seasonm
|
|
op = cardSet[r.ID]
|
|
case model.GotoUpRcmdS:
|
|
op.Limit = upAvCount[r.ID]
|
|
}
|
|
h.From(main, op)
|
|
if h.Get() == nil {
|
|
continue
|
|
}
|
|
h.Get().FromType = card.FromType
|
|
if h.Get().Right {
|
|
switch card.FromType {
|
|
case _fTypeOperation:
|
|
h.Get().ThreePointWatchLater()
|
|
case _fTypeRecommend:
|
|
h.Get().ThreePointChannel()
|
|
}
|
|
switch r.Goto {
|
|
case model.GotoTopstick:
|
|
top = h
|
|
default:
|
|
is = append(is, h)
|
|
}
|
|
}
|
|
// infoc
|
|
tinfo := &feed.Item{
|
|
Goto: card.Type,
|
|
Param: strconv.FormatInt(card.Value, 10),
|
|
URI: h.Get().URI,
|
|
FromType: card.FromType,
|
|
}
|
|
infocs = append(infocs, tinfo)
|
|
}
|
|
rl := len(is)
|
|
if rl == 0 {
|
|
is = []cardm.Handler{}
|
|
return
|
|
}
|
|
if idx == 0 {
|
|
idx = now.Unix()
|
|
}
|
|
for i, h := range is {
|
|
if pull {
|
|
h.Get().Idx = idx + int64(rl-i)
|
|
} else {
|
|
h.Get().Idx = idx - int64(i+1)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// ArchivesWithPlayer archives witch player
|
|
func (s *Service) ArchivesWithPlayer(c context.Context, aids []int64, qn int, platform string, fnver, fnval, build int) (res map[int64]*archive.ArchiveWithPlayer, err error) {
|
|
if res, err = s.arc.ArchivesWithPlayer(c, aids, qn, platform, fnver, fnval, build); err != nil {
|
|
log.Error("%+v", err)
|
|
}
|
|
if len(res) != 0 {
|
|
return
|
|
}
|
|
am, err := s.arc.Archives(c, aids)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if len(am) == 0 {
|
|
return
|
|
}
|
|
res = make(map[int64]*archive.ArchiveWithPlayer, len(am))
|
|
for aid, a := range am {
|
|
res[aid] = &archive.ArchiveWithPlayer{Archive3: archive.BuildArchive3(a)}
|
|
}
|
|
return
|
|
}
|
|
|
|
// isShowOperationCards is show operation cards by buvid
|
|
func (s *Service) isShowOperationCards(c context.Context, buvid string, channelID int64, cards []*card.Card, now time.Time) (isShow bool) {
|
|
var (
|
|
md5, mcmd5 string
|
|
)
|
|
g, ctx := errgroup.WithContext(c)
|
|
g.Go(func() (err error) {
|
|
if mcmd5, err = s.cd.ChannelCardCache(ctx, buvid, channelID); err != nil {
|
|
isShow = true
|
|
return
|
|
}
|
|
return nil
|
|
})
|
|
g.Go(func() (err error) {
|
|
md5 = s.hashCards(cards)
|
|
return nil
|
|
})
|
|
g.Wait()
|
|
if md5 != mcmd5 {
|
|
isShow = true
|
|
s.cd.AddChannelCardCache(c, buvid, md5, channelID, now)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (s *Service) hashCards(v []*card.Card) string {
|
|
bs, err := json.Marshal(v)
|
|
if err != nil {
|
|
log.Error("json.Marshal error(%v)", err)
|
|
return ""
|
|
}
|
|
return strconv.FormatUint(farm.Hash64(bs), 10)
|
|
}
|