2019-04-22 02:59:20 +00:00

1303 lines
38 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package search
import (
"context"
"fmt"
"strconv"
"time"
"go-common/app/interface/main/app-interface/conf"
accdao "go-common/app/interface/main/app-interface/dao/account"
arcdao "go-common/app/interface/main/app-interface/dao/archive"
artdao "go-common/app/interface/main/app-interface/dao/article"
bangumidao "go-common/app/interface/main/app-interface/dao/bangumi"
bplusdao "go-common/app/interface/main/app-interface/dao/bplus"
livedao "go-common/app/interface/main/app-interface/dao/live"
resdao "go-common/app/interface/main/app-interface/dao/resource"
srchdao "go-common/app/interface/main/app-interface/dao/search"
tagdao "go-common/app/interface/main/app-interface/dao/tag"
"go-common/app/interface/main/app-interface/model"
"go-common/app/interface/main/app-interface/model/bangumi"
"go-common/app/interface/main/app-interface/model/banner"
"go-common/app/interface/main/app-interface/model/bplus"
"go-common/app/interface/main/app-interface/model/live"
"go-common/app/interface/main/app-interface/model/search"
tagmdl "go-common/app/interface/main/app-interface/model/tag"
article "go-common/app/interface/openplatform/article/model"
account "go-common/app/service/main/account/model"
"go-common/app/service/main/archive/api"
resmdl "go-common/app/service/main/resource/model"
seasongrpc "go-common/app/service/openplatform/pgc-season/api/grpc/season/v1"
"go-common/library/log"
"go-common/library/sync/errgroup"
)
// const for search
const (
_oldAndroid = 514000
_oldIOS = 6090
IPhoneSearchResourceID = 2447
AndroidSearchResourceID = 2450
IPadSearchResourceID = 2811
)
var (
_emptyItem = []*search.Item{}
_emptyResult = &search.Result{
NavInfo: []*search.NavInfo{},
Page: 0,
Items: search.ResultItems{
Season: _emptyItem,
Upper: _emptyItem,
Movie: _emptyItem,
Archive: _emptyItem,
LiveRoom: _emptyItem,
LiveUser: _emptyItem,
},
}
)
// Service is search service
type Service struct {
c *conf.Config
srchDao *srchdao.Dao
accDao *accdao.Dao
arcDao *arcdao.Dao
liveDao *livedao.Dao
artDao *artdao.Dao
resDao *resdao.Dao
tagDao *tagdao.Dao
bangumiDao *bangumidao.Dao
bplusDao *bplusdao.Dao
// config
seasonNum int
movieNum int
seasonShowMore int
movieShowMore int
upUserNum int
uvLimit int
userNum int
userVideoLimit int
biliUserNum int
biliUserVideoLimit int
iPadSearchBangumi int
iPadSearchFt int
}
// New is search service initial func
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
srchDao: srchdao.New(c),
accDao: accdao.New(c),
liveDao: livedao.New(c),
arcDao: arcdao.New(c),
artDao: artdao.New(c),
resDao: resdao.New(c),
tagDao: tagdao.New(c),
bangumiDao: bangumidao.New(c),
bplusDao: bplusdao.New(c),
seasonNum: c.Search.SeasonNum,
movieNum: c.Search.MovieNum,
seasonShowMore: c.Search.SeasonMore,
movieShowMore: c.Search.MovieMore,
upUserNum: c.Search.UpUserNum,
uvLimit: c.Search.UVLimit,
userNum: c.Search.UpUserNum,
userVideoLimit: c.Search.UVLimit,
biliUserNum: c.Search.BiliUserNum,
biliUserVideoLimit: c.Search.BiliUserVideoLimit,
iPadSearchBangumi: c.Search.IPadSearchBangumi,
iPadSearchFt: c.Search.IPadSearchFt,
}
return
}
// Search get all type search data.
func (s *Service) Search(c context.Context, mid, zoneid int64, mobiApp, device, platform, buvid, keyword, duration, order, filtered, lang, fromSource, recommend, parent string, plat int8, rid, highlight, build, pn, ps, isQuery int, old bool, now time.Time) (res *search.Result, err error) {
const (
_newIPhonePGC = 6500
_newAndroidPGC = 519010
_newIPhoneSearch = 6500
_newAndroidSearch = 5215000
_newAndroidBSearch = 591200
)
var (
newPGC, flow, isNewTwitter bool
avids []int64
avm map[int64]*api.Arc
owners []int64
follows map[int64]bool
roomIDs []int64
lm map[int64]*live.RoomInfo
seasonIDs []int64
bangumis map[string]*bangumi.Card
//tagSeasonIDs []int32
tagBangumis map[int32]*seasongrpc.CardInfoProto
tags []int64
tagMyInfos []*tagmdl.Tag
dynamicIDs []int64
dynamicDetails map[int64]*bplus.Detail
accInfos map[int64]*account.Info
cooperation bool
)
// android 概念版 591205
if (plat == model.PlatAndroid && build >= _newAndroidPGC && build != 591205) || (plat == model.PlatIPhone && build >= _newIPhonePGC && build != 7140) || (plat == model.PlatAndroidB && build >= _newAndroidBSearch) || (plat == model.PlatIPad && build >= search.SearchNewIPad) || (plat == model.PlatIpadHD && build >= search.SearchNewIPadHD) || model.IsIPhoneB(plat) {
newPGC = true
}
// 处理一个ios概念版是 7140是否需要过滤
if (plat == model.PlatAndroid && build >= _newAndroidSearch) || (plat == model.PlatIPhone && build >= _newIPhoneSearch && build != 7140) || (plat == model.PlatAndroidB && build >= _newAndroidBSearch) || model.IsIPhoneB(plat) {
flow = true
}
var (
seasonNum int
movieNum int
)
if (plat == model.PlatIPad && build >= search.SearchNewIPad) || (plat == model.PlatIpadHD && build >= search.SearchNewIPadHD) {
seasonNum = s.iPadSearchBangumi
movieNum = s.iPadSearchFt
} else {
seasonNum = s.seasonNum
movieNum = s.movieNum
}
all, code, err := s.srchDao.Search(c, mid, zoneid, mobiApp, device, platform, buvid, keyword, duration, order, filtered, fromSource, recommend, parent, plat, seasonNum, movieNum, s.upUserNum, s.uvLimit, s.userNum, s.userVideoLimit, s.biliUserNum, s.biliUserVideoLimit, rid, highlight, build, pn, ps, isQuery, old, now, newPGC, flow)
if err != nil {
log.Error("%+v", err)
return
}
if (model.IsAndroid(plat) && build > s.c.SearchBuildLimit.NewTwitterAndroid) || (model.IsIPhone(plat) && build > s.c.SearchBuildLimit.NewTwitterIOS) {
isNewTwitter = true
}
if code == model.ForbidCode || code == model.NoResultCode {
res = _emptyResult
err = nil
return
}
res = &search.Result{}
res.Trackid = all.Trackid
res.Page = all.Page
res.Array = all.FlowPlaceholder
res.Attribute = all.Attribute
res.NavInfo = s.convertNav(all, plat, build, lang, old, newPGC)
if len(all.FlowResult) != 0 {
var item []*search.Item
for _, v := range all.FlowResult {
switch v.Type {
case search.TypeUser, search.TypeBiliUser:
owners = append(owners, v.User.Mid)
for _, vr := range v.User.Res {
avids = append(avids, vr.Aid)
}
roomIDs = append(roomIDs, v.User.RoomID)
case search.TypeVideo:
avids = append(avids, v.Video.ID)
case search.TypeLive:
roomIDs = append(roomIDs, v.Live.RoomID)
case search.TypeMediaBangumi, search.TypeMediaFt:
seasonIDs = append(seasonIDs, v.Media.SeasonID)
case search.TypeStar:
if v.Star.MID != 0 {
owners = append(owners, v.Star.MID)
}
if v.Star.TagID != 0 {
tags = append(tags, v.Star.TagID)
}
case search.TypeArticle:
owners = append(owners, v.Article.Mid)
case search.TypeChannel:
tags = append(tags, v.Channel.TagID)
if len(v.Channel.Values) > 0 {
for _, vc := range v.Channel.Values {
switch vc.Type {
case search.TypeVideo:
if vc.Video != nil {
avids = append(avids, vc.Video.ID)
}
//case search.TypeLive:
// if vc.Live != nil {
// roomIDs = append(roomIDs, vc.Live.RoomID)
// }
//case search.TypeMediaBangumi, search.TypeMediaFt:
// if vc.Media != nil {
// tagSeasonIDs = append(tagSeasonIDs, int32(vc.Media.SeasonID))
// }
}
}
}
case search.TypeTwitter:
dynamicIDs = append(dynamicIDs, v.Twitter.ID)
}
}
g, ctx := errgroup.WithContext(c)
if len(owners) != 0 {
if mid > 0 {
g.Go(func() error {
follows = s.accDao.Relations3(ctx, owners, mid)
return nil
})
}
g.Go(func() (err error) {
if accInfos, err = s.accDao.Infos3(ctx, owners); err != nil {
log.Error("%v", err)
err = nil
}
return
})
}
if len(avids) != 0 {
g.Go(func() (err error) {
if avm, err = s.arcDao.Archives2(ctx, avids); err != nil {
log.Error("%+v", err)
err = nil
}
return
})
}
if len(roomIDs) != 0 {
g.Go(func() (err error) {
if lm, err = s.liveDao.LiveByRIDs(ctx, roomIDs); err != nil {
log.Error("%+v", err)
err = nil
}
return
})
}
if len(seasonIDs) != 0 {
g.Go(func() (err error) {
if bangumis, err = s.bangumiDao.Card(ctx, mid, seasonIDs); err != nil {
log.Error("%+v", err)
err = nil
}
return
})
}
//if len(tagSeasonIDs) != 0 {
// g.Go(func() (err error) {
// if tagBangumis, err = s.bangumiDao.Cards(ctx, tagSeasonIDs); err != nil {
// log.Error("%+v", err)
// err = nil
// }
// return
// })
//}
if len(tags) != 0 {
g.Go(func() (err error) {
if tagMyInfos, err = s.tagDao.TagInfos(ctx, tags, mid); err != nil {
log.Error("%v \n", err)
err = nil
}
return
})
}
if len(dynamicIDs) != 0 {
g.Go(func() (err error) {
if dynamicDetails, err = s.bplusDao.DynamicDetails(ctx, dynamicIDs, "search"); err != nil {
log.Error("%v \n", err)
err = nil
}
return
})
}
if err = g.Wait(); err != nil {
log.Error("%+v", err)
return
}
if all.SuggestKeyword != "" && pn == 1 {
i := &search.Item{Title: all.SuggestKeyword, Goto: model.GotoSuggestKeyWord, SugKeyWordType: 1}
item = append(item, i)
} else if all.CrrQuery != "" && pn == 1 {
if (model.IsAndroid(plat) && build > s.c.SearchBuildLimit.QueryCorAndroid) || (model.IsIPhone(plat) && build > s.c.SearchBuildLimit.QueryCorIOS) {
i := &search.Item{Title: fmt.Sprintf("已匹配%q的搜索结果", all.CrrQuery), Goto: model.GotoSuggestKeyWord, SugKeyWordType: 2}
item = append(item, i)
}
}
for _, v := range all.FlowResult {
i := &search.Item{TrackID: v.TrackID, LinkType: v.LinkType, Position: v.Position}
switch v.Type {
case search.TypeVideo:
if (model.IsAndroid(plat) && build > s.c.SearchBuildLimit.CooperationAndroid) || (model.IsIPhone(plat) && build > s.c.SearchBuildLimit.CooperationIOS) {
cooperation = true
}
i.FromVideo(v.Video, avm[v.Video.ID], cooperation)
case search.TypeLive:
i.FromLive(v.Live, lm[v.Live.RoomID])
case search.TypeMediaBangumi:
i.FromMedia(v.Media, "", model.GotoBangumi, bangumis)
case search.TypeMediaFt:
i.FromMedia(v.Media, "", model.GotoMovie, bangumis)
case search.TypeArticle:
i.FromArticle(v.Article, accInfos[v.Article.Mid])
case search.TypeSpecial:
i.FromOperate(v.Operate, model.GotoSpecial)
case search.TypeBanner:
i.FromOperate(v.Operate, model.GotoBanner)
case search.TypeUser:
if follows[v.User.Mid] {
i.Attentions = 1
}
i.FromUser(v.User, avm, lm[v.User.RoomID])
case search.TypeBiliUser:
if follows[v.User.Mid] {
i.Attentions = 1
}
i.FromUpUser(v.User, avm, lm[v.User.RoomID])
case search.TypeSpecialS:
i.FromOperate(v.Operate, model.GotoSpecialS)
case search.TypeGame:
i.FromGame(v.Game)
case search.TypeQuery:
i.Title = v.TypeName
i.FromQuery(v.Query)
case search.TypeComic:
i.FromComic(v.Comic)
case search.TypeConverge:
var (
aids, rids, artids []int64
am map[int64]*api.Arc
rm map[int64]*live.Room
artm map[int64]*article.Meta
)
for _, c := range v.Operate.ContentList {
switch c.Type {
case 0:
aids = append(aids, c.ID)
case 1:
rids = append(rids, c.ID)
case 2:
artids = append(artids, c.ID)
}
}
g, ctx := errgroup.WithContext(c)
if len(aids) != 0 {
g.Go(func() (err error) {
if am, err = s.arcDao.Archives2(ctx, aids); err != nil {
log.Error("%+v", err)
err = nil
}
return
})
}
if len(rids) != 0 {
g.Go(func() (err error) {
if rm, err = s.liveDao.AppMRoom(ctx, rids); err != nil {
log.Error("%+v", err)
err = nil
}
return
})
}
if len(artids) != 0 {
g.Go(func() (err error) {
if artm, err = s.artDao.Articles(ctx, artids); err != nil {
log.Error("%+v", err)
err = nil
}
return
})
}
if err = g.Wait(); err != nil {
log.Error("%+v", err)
continue
}
i.FromConverge(v.Operate, am, rm, artm)
case search.TypeTwitter:
i.FromTwitter(v.Twitter, dynamicDetails, s.c.SearchDynamicSwitch.IsUP, s.c.SearchDynamicSwitch.IsCount, isNewTwitter)
case search.TypeStar:
if v.Star.TagID != 0 {
i.URIType = search.StarChannel
for _, myInfo := range tagMyInfos {
if myInfo != nil && myInfo.TagID == v.Star.TagID {
i.IsAttention = myInfo.IsAtten
break
}
}
} else if v.Star.MID != 0 {
i.URIType = search.StarSpace
if follows[v.Star.MID] {
i.IsAttention = 1
}
}
i.FromStar(v.Star)
case search.TypeTicket:
i.FromTicket(v.Ticket)
case search.TypeProduct:
i.FromProduct(v.Product)
case search.TypeSpecialerGuide:
i.FromSpecialerGuide(v.SpecialerGuide)
case search.TypeChannel:
i.FromChannel(v.Channel, avm, tagBangumis, lm, tagMyInfos)
}
if i.Goto != "" {
item = append(item, i)
}
}
res.Item = item
if plat == model.PlatAndroid && build < search.SearchEggInfoAndroid {
return
}
if all.EggInfo != nil {
res.EasterEgg = &search.EasterEgg{ID: all.EggInfo.Source, ShowCount: all.EggInfo.ShowCount}
}
return
}
var items []*search.Item
if all.SuggestKeyword != "" && pn == 1 {
res.Items.SuggestKeyWord = &search.Item{Title: all.SuggestKeyword, Goto: model.GotoSuggestKeyWord}
}
// archive
for _, v := range all.Result.Video {
avids = append(avids, v.ID)
}
if duration == "0" && order == "totalrank" && rid == 0 {
for _, v := range all.Result.Movie {
if v.Type == "movie" {
avids = append(avids, v.Aid)
}
}
}
if pn == 1 {
for _, v := range all.Result.User {
for _, vr := range v.Res {
avids = append(avids, vr.Aid)
}
}
if old {
for _, v := range all.Result.UpUser {
for _, vr := range v.Res {
avids = append(avids, vr.Aid)
}
owners = append(owners, v.Mid)
roomIDs = append(roomIDs, v.RoomID)
}
} else {
for _, v := range all.Result.BiliUser {
for _, vr := range v.Res {
avids = append(avids, vr.Aid)
}
owners = append(owners, v.Mid)
roomIDs = append(roomIDs, v.RoomID)
}
}
}
if model.IsOverseas(plat) {
for _, v := range all.Result.LiveRoom {
roomIDs = append(roomIDs, v.RoomID)
}
for _, v := range all.Result.LiveUser {
roomIDs = append(roomIDs, v.RoomID)
}
}
g, ctx := errgroup.WithContext(c)
if len(owners) != 0 {
if mid > 0 {
g.Go(func() error {
follows = s.accDao.Relations3(ctx, owners, mid)
return nil
})
}
}
if len(avids) != 0 {
g.Go(func() (err error) {
if avm, err = s.arcDao.Archives2(ctx, avids); err != nil {
log.Error("%+v", err)
err = nil
}
return
})
}
if len(roomIDs) != 0 {
g.Go(func() (err error) {
if lm, err = s.liveDao.LiveByRIDs(ctx, roomIDs); err != nil {
log.Error("%+v", err)
err = nil
}
return
})
}
if err = g.Wait(); err != nil {
log.Error("%+v", err)
return
}
if duration == "0" && order == "totalrank" && rid == 0 {
var promptBangumi, promptFt string
// season
bangumi := all.Result.Bangumi
items = make([]*search.Item, 0, len(bangumi))
for _, v := range bangumi {
si := &search.Item{}
if (model.IsAndroid(plat) && build <= _oldAndroid) || (model.IsIPhone(plat) && build <= _oldIOS) {
si.FromSeason(v, model.GotoBangumi)
} else {
si.FromSeason(v, model.GotoBangumiWeb)
}
items = append(items, si)
}
res.Items.Season = items
// movie
movie := all.Result.Movie
items = make([]*search.Item, 0, len(movie))
for _, v := range movie {
si := &search.Item{}
si.FromMovie(v, avm)
items = append(items, si)
}
res.Items.Movie = items
// season2
mb := all.Result.MediaBangumi
items = make([]*search.Item, 0, len(mb))
for k, v := range mb {
si := &search.Item{}
if ((plat == model.PlatIPad && build >= search.SearchNewIPad) || (plat == model.PlatIpadHD && build >= search.SearchNewIPadHD)) && (k == len(mb)-1) && all.PageInfo.MediaBangumi.NumResults > s.iPadSearchBangumi {
promptBangumi = fmt.Sprintf("查看全部番剧 ( %d ) >", all.PageInfo.MediaBangumi.NumResults)
}
si.FromMedia(v, promptBangumi, model.GotoBangumi, nil)
items = append(items, si)
}
res.Items.Season2 = items
// movie2
mf := all.Result.MediaFt
items = make([]*search.Item, 0, len(mf))
for k, v := range mf {
si := &search.Item{}
if ((plat == model.PlatIPad && build >= search.SearchNewIPad) || (plat == model.PlatIpadHD && build >= search.SearchNewIPadHD)) && (k == len(mf)-1) && all.PageInfo.MediaFt.NumResults > s.iPadSearchFt {
promptFt = fmt.Sprintf("查看全部影视 ( %d ) >", all.PageInfo.MediaFt.NumResults)
}
si.FromMedia(v, promptFt, model.GotoMovie, nil)
si.Goto = model.GotoAv
items = append(items, si)
}
res.Items.Movie2 = items
}
if pn == 1 {
// upper + user
var tmp []*search.User
if old {
tmp = all.Result.UpUser
} else {
tmp = all.Result.BiliUser
}
items = make([]*search.Item, 0, len(tmp)+len(all.Result.User))
for _, v := range all.Result.User {
si := &search.Item{}
si.FromUser(v, avm, lm[v.RoomID])
if follows[v.Mid] {
si.Attentions = 1
}
items = append(items, si)
}
if len(items) == 0 {
for _, v := range tmp {
si := &search.Item{}
si.FromUpUser(v, avm, lm[v.RoomID])
if follows[v.Mid] {
si.Attentions = 1
}
if old {
si.IsUp = true
}
items = append(items, si)
}
}
res.Items.Upper = items
}
items = make([]*search.Item, 0, len(all.Result.Video))
for _, v := range all.Result.Video {
si := &search.Item{}
si.FromVideo(v, avm[v.ID], cooperation)
items = append(items, si)
}
res.Items.Archive = items
// live room
if model.IsOverseas(plat) {
items = make([]*search.Item, 0, len(all.Result.LiveRoom))
for _, v := range all.Result.LiveRoom {
si := &search.Item{}
si.FromLive(v, lm[v.RoomID])
items = append(items, si)
}
res.Items.LiveRoom = items
// live user
items = make([]*search.Item, 0, len(all.Result.LiveUser))
for _, v := range all.Result.LiveUser {
si := &search.Item{}
si.FromLive(v, lm[v.RoomID])
items = append(items, si)
}
res.Items.LiveUser = items
}
return
}
// SearchByType is tag bangumi movie upuser video search
func (s *Service) SearchByType(c context.Context, mid, zoneid int64, mobiApp, device, platform, buvid, sType, keyword, filtered, order string, plat int8, build, highlight, categoryID, userType, orderSort, pn, ps int, old bool, now time.Time) (res *search.TypeSearch, err error) {
switch sType {
case "season":
if res, err = s.srchDao.Season(c, mid, zoneid, keyword, mobiApp, device, platform, buvid, filtered, plat, build, pn, ps, now); err != nil {
return
}
case "upper":
if res, err = s.upper(c, mid, keyword, mobiApp, device, platform, buvid, filtered, order, s.biliUserVideoLimit, highlight, build, userType, orderSort, pn, ps, old, now); err != nil {
return
}
case "movie":
if !model.IsOverseas(plat) {
if res, err = s.srchDao.MovieByType(c, mid, zoneid, keyword, mobiApp, device, platform, buvid, filtered, plat, build, pn, ps, now); err != nil {
return
}
}
case "live_room", "live_user":
if res, err = s.srchDao.LiveByType(c, mid, zoneid, keyword, mobiApp, device, platform, buvid, filtered, order, sType, plat, build, pn, ps, now); err != nil {
return
}
case "article":
if res, err = s.article(c, mid, zoneid, highlight, keyword, mobiApp, device, platform, buvid, filtered, order, sType, plat, categoryID, build, pn, ps, now); err != nil {
return
}
case "season2":
if (model.IsAndroid(plat) && build <= s.c.SearchBuildLimit.PGCHighLightAndroid) || (model.IsIOS(plat) && build <= s.c.SearchBuildLimit.PGCHighLightIOS) {
highlight = 0
}
if res, err = s.srchDao.Season2(c, mid, keyword, mobiApp, device, platform, buvid, highlight, build, pn, ps); err != nil {
return
}
case "movie2":
if !model.IsOverseas(plat) {
if (model.IsAndroid(plat) && build <= s.c.SearchBuildLimit.PGCHighLightAndroid) || (model.IsIOS(plat) && build <= s.c.SearchBuildLimit.PGCHighLightIOS) {
highlight = 0
}
if res, err = s.srchDao.MovieByType2(c, mid, keyword, mobiApp, device, platform, buvid, highlight, build, pn, ps); err != nil {
return
}
}
case "tag":
if res, err = s.channel(c, mid, keyword, mobiApp, platform, buvid, device, order, sType, build, pn, ps, highlight); err != nil {
return
}
case "video":
if res, err = s.srchDao.Video(c, mid, keyword, mobiApp, device, platform, buvid, highlight, build, pn, ps); err != nil {
return
}
}
if res == nil {
res = &search.TypeSearch{Items: []*search.Item{}}
}
return
}
// SearchLive is search live
func (s *Service) SearchLive(c context.Context, mid int64, mobiApp, platform, buvid, device, sType, keyword, order string, build, pn, ps int) (res *search.TypeSearch, err error) {
if res, err = s.srchDao.Live(c, mid, keyword, mobiApp, platform, buvid, device, order, sType, build, pn, ps); err != nil {
return
}
if res == nil {
res = &search.TypeSearch{Items: []*search.Item{}}
}
return
}
// SearchLiveAll is search live
func (s *Service) SearchLiveAll(c context.Context, mid int64, mobiApp, platform, buvid, device, sType, keyword, order string, build, pn, ps int) (res *search.TypeSearchLiveAll, err error) {
var (
g *errgroup.Group
ctx context.Context
uid int64
owners []int64
glorys []*live.Glory
follows map[int64]bool
userInfos map[int64]map[string]*live.Exp
)
if res, err = s.srchDao.LiveAll(c, mid, keyword, mobiApp, platform, buvid, device, order, sType, build, pn, ps); err != nil {
return
}
if res.Master != nil {
for _, item := range res.Master.Items {
uid = item.Mid
owners = append(owners, uid)
break
}
}
if len(owners) != 0 {
g, ctx = errgroup.WithContext(c)
if uid > 0 {
g.Go(func() error {
follows = s.accDao.Relations3(ctx, owners, mid)
return nil
})
g.Go(func() error {
glorys, _ = s.liveDao.Glory(ctx, uid)
return nil
})
g.Go(func() error {
userInfos, _ = s.liveDao.UserInfo(ctx, owners)
return nil
})
}
if err = g.Wait(); err != nil {
log.Error("%+v", err)
return
}
for _, m := range res.Master.Items {
if follows[uid] {
m.IsAttention = 1
}
m.Glory = &search.Glory{
Title: "主播荣誉",
Total: len(glorys),
Items: make([]*search.Item, 0, len(glorys)),
}
if userInfo, ok := userInfos[m.Mid]; ok {
if u, ok := userInfo["exp"]; ok {
if u != nil || u.Master != nil {
m.Level = u.Master.Level
m.LevelColor = u.Master.Color
}
}
}
for _, glory := range glorys {
if glory.GloryInfo != nil {
item := &search.Item{
Title: glory.GloryInfo.Name,
Cover: glory.GloryInfo.Cover,
}
m.Glory.Items = append(m.Glory.Items, item)
}
}
}
}
if res == nil {
res = &search.TypeSearchLiveAll{Master: &search.TypeSearch{Items: []*search.Item{}}, Room: &search.TypeSearch{Items: []*search.Item{}}}
}
return
}
// channel search for channel
func (s *Service) channel(c context.Context, mid int64, keyword, mobiApp, platform, buvid, device, order, sType string, build, pn, ps, highlight int) (res *search.TypeSearch, err error) {
var (
g *errgroup.Group
ctx context.Context
tags []int64
tagMyInfos []*tagmdl.Tag
)
if res, err = s.srchDao.Channel(c, mid, keyword, mobiApp, platform, buvid, device, order, sType, build, pn, ps, highlight); err != nil {
return
}
if res == nil || len(res.Items) == 0 {
return
}
tags = make([]int64, 0, len(res.Items))
for _, item := range res.Items {
tags = append(tags, item.ID)
}
if len(tags) != 0 {
g, ctx = errgroup.WithContext(c)
if mid > 0 {
g.Go(func() error {
tagMyInfos, _ = s.tagDao.TagInfos(ctx, tags, mid)
return nil
})
}
if err = g.Wait(); err != nil {
log.Error("%+v", err)
return
}
for _, item := range res.Items {
for _, myInfo := range tagMyInfos {
if myInfo != nil && myInfo.TagID == item.ID {
item.IsAttention = myInfo.IsAtten
break
}
}
}
}
return
}
// upper search for upper
func (s *Service) upper(c context.Context, mid int64, keyword, mobiApp, device, platform, buvid, filtered, order string, biliUserVL, highlight, build, userType, orderSort, pn, ps int, old bool, now time.Time) (res *search.TypeSearch, err error) {
var (
g *errgroup.Group
ctx context.Context
owners []int64
follows map[int64]bool
)
if res, err = s.srchDao.Upper(c, mid, keyword, mobiApp, device, platform, buvid, filtered, order, biliUserVL, highlight, build, userType, orderSort, pn, ps, old, now); err != nil {
return
}
if res == nil || len(res.Items) == 0 {
return
}
owners = make([]int64, 0, len(res.Items))
for _, item := range res.Items {
owners = append(owners, item.Mid)
}
if len(owners) != 0 {
g, ctx = errgroup.WithContext(c)
if mid > 0 {
g.Go(func() error {
follows = s.accDao.Relations3(ctx, owners, mid)
return nil
})
}
if err = g.Wait(); err != nil {
log.Error("%+v", err)
return
}
for _, item := range res.Items {
if follows[item.Mid] {
item.Attentions = 1
}
}
}
return
}
// article search for article
func (s *Service) article(c context.Context, mid, zoneid int64, highlight int, keyword, mobiApp, device, platform, buvid, filtered, order, sType string, plat int8, categoryID, build, pn, ps int, now time.Time) (res *search.TypeSearch, err error) {
if res, err = s.srchDao.ArticleByType(c, mid, zoneid, keyword, mobiApp, device, platform, buvid, filtered, order, sType, plat, categoryID, build, highlight, pn, ps, now); err != nil {
log.Error("%+v", err)
return
}
if res != nil && len(res.Items) > 0 {
var mids []int64
for _, v := range res.Items {
mids = append(mids, v.Mid)
}
var infom map[int64]*account.Info
if infom, err = s.accDao.Infos3(c, mids); err != nil {
log.Error("%+v", err)
err = nil
return
}
for _, item := range res.Items {
if info, ok := infom[item.Mid]; ok {
item.Name = info.Name
}
}
}
return
}
// HotSearch is hot word search
func (s *Service) HotSearch(c context.Context, buvid string, mid int64, build, limit int, mobiApp, device, platform string, now time.Time) (res *search.Hot) {
var err error
if res, err = s.srchDao.HotSearch(c, buvid, mid, build, limit, mobiApp, device, platform, now); err != nil {
log.Error("%+v", err)
}
if res != nil {
res.TrackID = res.SeID
res.SeID = ""
res.Code = 0
} else {
res = &search.Hot{}
}
return
}
// Suggest for search suggest
func (s *Service) Suggest(c context.Context, mid int64, buvid, keyword string, build int, mobiApp, device string, now time.Time) (res *search.Suggestion) {
var (
suggest *search.Suggest
err error
)
res = &search.Suggestion{}
if suggest, err = s.srchDao.Suggest(c, mid, buvid, keyword, build, mobiApp, device, now); err != nil {
log.Error("%+v", err)
return
}
if suggest != nil {
res.UpUser = suggest.Result.Accurate.UpUser
res.Bangumi = suggest.Result.Accurate.Bangumi
for _, v := range suggest.Result.Tag {
res.Suggest = append(res.Suggest, v.Value)
}
res.TrackID = suggest.Stoken
}
return
}
// Suggest2 for search suggest
func (s *Service) Suggest2(c context.Context, mid int64, platform, buvid, keyword string, build int, mobiApp string, now time.Time) (res *search.Suggestion2) {
var (
suggest *search.Suggest2
err error
avids []int64
avm map[int64]*api.Arc
roomIDs []int64
lm map[int64]*live.RoomInfo
)
res = &search.Suggestion2{}
if suggest, err = s.srchDao.Suggest2(c, mid, platform, buvid, keyword, build, mobiApp, now); err != nil {
log.Error("%+v", err)
return
}
if suggest.Result != nil {
for _, v := range suggest.Result.Tag {
if v.SpID == search.SuggestionJump {
if v.Type == search.SuggestionAV {
avids = append(avids, v.Ref)
}
if v.Type == search.SuggestionLive {
roomIDs = append(roomIDs, v.Ref)
}
}
}
g, ctx := errgroup.WithContext(c)
if len(avids) != 0 {
g.Go(func() (err error) {
if avm, err = s.arcDao.Archives2(ctx, avids); err != nil {
log.Error("%+v", err)
err = nil
}
return
})
}
if len(roomIDs) != 0 {
g.Go(func() (err error) {
if lm, err = s.liveDao.LiveByRIDs(ctx, roomIDs); err != nil {
log.Error("%+v", err)
err = nil
}
return
})
}
if err = g.Wait(); err != nil {
log.Error("%+v", err)
return
}
for _, v := range suggest.Result.Tag {
si := &search.Item{}
si.FromSuggest2(v, avm, lm)
res.List = append(res.List, si)
}
res.TrackID = suggest.Stoken
}
return
}
// Suggest3 for search suggest
func (s *Service) Suggest3(c context.Context, mid int64, platform, buvid, keyword, device string, build, highlight int, mobiApp string, now time.Time) (res *search.SuggestionResult3) {
var (
suggest *search.Suggest3
err error
avids []int64
avm map[int64]*api.Arc
roomIDs []int64
lm map[int64]*live.RoomInfo
)
res = &search.SuggestionResult3{}
if suggest, err = s.srchDao.Suggest3(c, mid, platform, buvid, keyword, device, build, highlight, mobiApp, now); err != nil {
log.Error("%+v", err)
return
}
for _, v := range suggest.Result {
if v.TermType == search.SuggestionJump {
if v.SubType == search.SuggestionAV {
avids = append(avids, v.Ref)
}
if v.SubType == search.SuggestionLive {
roomIDs = append(roomIDs, v.Ref)
}
}
}
g, ctx := errgroup.WithContext(c)
if len(avids) != 0 {
g.Go(func() (err error) {
if avm, err = s.arcDao.Archives2(ctx, avids); err != nil {
log.Error("%+v", err)
err = nil
}
return
})
}
if len(roomIDs) != 0 {
g.Go(func() (err error) {
if lm, err = s.liveDao.LiveByRIDs(ctx, roomIDs); err != nil {
log.Error("%+v", err)
err = nil
}
return
})
}
if err = g.Wait(); err != nil {
log.Error("%+v", err)
return
}
for _, v := range suggest.Result {
si := &search.Item{}
si.FromSuggest3(v, avm, lm)
res.List = append(res.List, si)
}
res.TrackID = suggest.TrackID
return
}
// User for search uer
func (s *Service) User(c context.Context, mid int64, buvid, mobiApp, device, platform, keyword, filtered, order, fromSource string, highlight, build, userType, orderSort, pn, ps int, now time.Time) (res *search.UserResult) {
res = &search.UserResult{}
user, err := s.srchDao.User(c, mid, keyword, mobiApp, device, platform, buvid, filtered, order, fromSource, highlight, build, userType, orderSort, pn, ps, now)
if err != nil {
log.Error("%+v", err)
return
}
if len(user) == 0 {
return
}
res.Items = make([]*search.Item, 0, len(user))
for _, u := range user {
res.Items = append(res.Items, &search.Item{Mid: u.Mid, Name: u.Name, Face: u.Pic})
}
return
}
// convertNav deal with old search pageinfo to new.
func (s *Service) convertNav(all *search.Search, plat int8, build int, lang string, old, newPGC bool) (nis []*search.NavInfo) {
const (
_showHide = 0
_oldAndroidArticle = 515009
)
var (
season = "番剧"
live = "直播"
upper = "用户"
movie = "影视"
liveroom = "直播间"
liveuser = "主播"
article = "专栏"
)
if old {
upper = "UP主"
}
if lang == model.Hant {
season = "番劇"
live = "直播"
upper = "UP主"
movie = "影視"
liveroom = "直播间"
liveuser = "主播"
article = "專欄"
}
nis = make([]*search.NavInfo, 0, 4)
// season
if !newPGC && all.PageInfo.Bangumi != nil {
var nav = &search.NavInfo{
Name: season,
Total: all.PageInfo.Bangumi.NumResults,
Pages: all.PageInfo.Bangumi.Pages,
Type: 1,
}
if all.PageInfo.Bangumi.NumResults > s.seasonNum {
nav.Show = s.seasonShowMore
} else {
nav.Show = _showHide
}
nis = append(nis, nav)
}
// media season
if newPGC && all.PageInfo.MediaBangumi != nil {
var nav = &search.NavInfo{
Name: season,
Total: all.PageInfo.MediaBangumi.NumResults,
Pages: all.PageInfo.MediaBangumi.Pages,
Type: 7,
}
if all.PageInfo.MediaBangumi.NumResults > s.seasonNum {
nav.Show = s.seasonShowMore
} else {
nav.Show = _showHide
}
nis = append(nis, nav)
}
// live
if (model.IsAndroid(plat) && build > search.SearchLiveAllAndroid) || (model.IsIPhone(plat) && build > search.SearchLiveAllIOS) || ((plat == model.PlatIPad && build >= search.SearchNewIPad) || (plat == model.PlatIpadHD && build >= search.SearchNewIPadHD)) || model.IsIPhoneB(plat) {
if all.PageInfo.LiveAll != nil {
var nav = &search.NavInfo{
Name: live,
Total: all.PageInfo.LiveAll.NumResults,
Pages: all.PageInfo.LiveAll.Pages,
Type: 4,
}
nis = append(nis, nav)
}
} else {
if all.PageInfo.LiveRoom != nil {
var nav = &search.NavInfo{
Name: live,
Total: all.PageInfo.LiveRoom.NumResults,
Pages: all.PageInfo.LiveRoom.Pages,
Type: 4,
}
nis = append(nis, nav)
}
}
// upper
if old {
if all.PageInfo.UpUser != nil {
var nav = &search.NavInfo{
Name: upper,
Total: all.PageInfo.UpUser.NumResults,
Pages: all.PageInfo.UpUser.Pages,
Type: 2,
}
nis = append(nis, nav)
}
} else {
if all.PageInfo.BiliUser != nil {
var nav = &search.NavInfo{
Name: upper,
Total: all.PageInfo.BiliUser.NumResults,
Pages: all.PageInfo.BiliUser.Pages,
Type: 2,
}
nis = append(nis, nav)
}
}
// movie
if !newPGC && all.PageInfo.Film != nil {
var nav = &search.NavInfo{
Name: movie,
Total: all.PageInfo.Film.NumResults,
Pages: all.PageInfo.Film.Pages,
Type: 3,
}
if all.PageInfo.Movie != nil && all.PageInfo.Movie.NumResults > s.movieNum {
nav.Show = s.movieShowMore
} else {
nav.Show = _showHide
}
nis = append(nis, nav)
}
// media movie
if newPGC && all.PageInfo.MediaFt != nil {
var nav = &search.NavInfo{
Name: movie,
Total: all.PageInfo.MediaFt.NumResults,
Pages: all.PageInfo.MediaFt.Pages,
Type: 8,
}
if all.PageInfo.MediaFt.NumResults > s.movieNum {
nav.Show = s.movieShowMore
} else {
nav.Show = _showHide
}
nis = append(nis, nav)
}
if model.IsOverseas(plat) {
// live room
if all.PageInfo.LiveRoom != nil {
var nav = &search.NavInfo{
Name: liveroom,
Total: all.PageInfo.LiveRoom.NumResults,
Pages: all.PageInfo.LiveRoom.Pages,
Type: 4,
}
nis = append(nis, nav)
}
if all.PageInfo.LiveUser != nil {
var nav = &search.NavInfo{
Name: liveuser,
Total: all.PageInfo.LiveUser.NumResults,
Pages: all.PageInfo.LiveUser.Pages,
Type: 5,
}
nis = append(nis, nav)
}
} else {
if all.PageInfo.Article != nil {
if (model.IsIPhone(plat) && build > _oldIOS) || (model.IsAndroid(plat) && build > _oldAndroidArticle) || model.IsIPhoneB(plat) {
var nav = &search.NavInfo{
Name: article,
Total: all.PageInfo.Article.NumResults,
Pages: all.PageInfo.Article.Pages,
Type: 6,
}
nis = append(nis, nav)
}
}
}
return
}
// RecommendNoResult search when no result
func (s *Service) RecommendNoResult(c context.Context, platform, mobiApp, device, buvid, keyword string, build, pn, ps int, mid int64) (res *search.NoResultRcndResult, err error) {
if res, err = s.srchDao.RecommendNoResult(c, platform, mobiApp, device, buvid, keyword, build, pn, ps, mid); err != nil {
log.Error("%+v", err)
}
return
}
// Recommend search recommend
func (s *Service) Recommend(c context.Context, mid int64, build, from, show int, buvid, platform, mobiApp, device string) (res *search.RecommendResult, err error) {
if res, err = s.srchDao.Recommend(c, mid, build, from, show, buvid, platform, mobiApp, device); err != nil {
log.Error("%+v", err)
}
return
}
// DefaultWords search for default words
func (s *Service) DefaultWords(c context.Context, mid int64, build, from int, buvid, platform, mobiApp, device string) (res *search.DefaultWords, err error) {
if res, err = s.srchDao.DefaultWords(c, mid, build, from, buvid, platform, mobiApp, device); err != nil {
log.Error("%+v", err)
}
return
}
// Resource for rsource
func (s *Service) Resource(c context.Context, mobiApp, device, network, buvid, adExtra string, build int, plat int8, mid int64) (res []*banner.Banner, err error) {
var (
bnsm map[int][]*resmdl.Banner
resID int
)
if model.IsAndroid(plat) {
resID = AndroidSearchResourceID
} else if model.IsIPhone(plat) {
resID = IPhoneSearchResourceID
} else if model.IsIPad(plat) {
resID = IPadSearchResourceID
}
if bnsm, err = s.resDao.Banner(c, mobiApp, device, network, "", buvid, adExtra, strconv.Itoa(resID), build, plat, mid); err != nil {
return
}
// only one position
for _, rb := range bnsm[resID] {
b := &banner.Banner{}
b.ChangeBanner(rb)
res = append(res, b)
break
}
return
}
// RecommendPre search at pre-page.
func (s *Service) RecommendPre(c context.Context, platform, mobiApp, device, buvid string, build, ps int, mid int64) (res *search.RecommendPreResult, err error) {
if res, err = s.srchDao.RecommendPre(c, platform, mobiApp, device, buvid, build, ps, mid); err != nil {
log.Error("%+v", err)
}
return
}
// SearchEpisodes search PGC episodes
func (s *Service) SearchEpisodes(c context.Context, mid, ssID int64) (res []*search.Item, err error) {
var (
seasonIDs []int64
bangumis map[string]*bangumi.Card
)
seasonIDs = []int64{ssID}
if bangumis, err = s.bangumiDao.Card(c, mid, seasonIDs); err != nil {
log.Error("%+v", err)
return
}
if bangumi, ok := bangumis[strconv.FormatInt(ssID, 10)]; ok {
for _, v := range bangumi.Episodes {
tmp := &search.Item{
Param: strconv.Itoa(int(v.ID)),
Index: v.Index,
Badges: v.Badges,
}
tmp.URI = model.FillURI(model.GotoEP, tmp.Param, nil)
res = append(res, tmp)
}
}
return
}