go-common/app/interface/main/app-channel/service/channel/index2.go
2019-04-22 18:49:16 +08:00

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)
}