545 lines
15 KiB
Go
545 lines
15 KiB
Go
package search
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
tag "go-common/app/interface/main/app-interface/model/tag"
|
|
"go-common/app/interface/main/app-intl/conf"
|
|
accdao "go-common/app/interface/main/app-intl/dao/account"
|
|
arcdao "go-common/app/interface/main/app-intl/dao/archive"
|
|
artdao "go-common/app/interface/main/app-intl/dao/article"
|
|
bgmdao "go-common/app/interface/main/app-intl/dao/bangumi"
|
|
resdao "go-common/app/interface/main/app-intl/dao/resource"
|
|
srchdao "go-common/app/interface/main/app-intl/dao/search"
|
|
tagdao "go-common/app/interface/main/app-intl/dao/tag"
|
|
"go-common/app/interface/main/app-intl/model"
|
|
"go-common/app/interface/main/app-intl/model/bangumi"
|
|
"go-common/app/interface/main/app-intl/model/search"
|
|
article "go-common/app/interface/openplatform/article/model"
|
|
account "go-common/app/service/main/account/model"
|
|
"go-common/app/service/main/archive/api"
|
|
"go-common/library/log"
|
|
"go-common/library/sync/errgroup"
|
|
)
|
|
|
|
var (
|
|
_emptyResult = &search.Result{
|
|
NavInfo: []*search.NavInfo{},
|
|
Page: 0,
|
|
}
|
|
)
|
|
|
|
// Service is search service
|
|
type Service struct {
|
|
c *conf.Config
|
|
srchDao *srchdao.Dao
|
|
accDao *accdao.Dao
|
|
arcDao *arcdao.Dao
|
|
artDao *artdao.Dao
|
|
// artDao *artdao.Dao
|
|
resDao *resdao.Dao
|
|
tagDao *tagdao.Dao
|
|
bgmDao *bgmdao.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),
|
|
arcDao: arcdao.New(c),
|
|
artDao: artdao.New(c),
|
|
// artDao: artdao.New(c),
|
|
resDao: resdao.New(c),
|
|
tagDao: tagdao.New(c),
|
|
bgmDao: bgmdao.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 string, plat int8, rid, highlight, build, pn, ps int, now time.Time) (res *search.Result, err error) {
|
|
var (
|
|
aids []int64
|
|
am map[int64]*api.Arc
|
|
owners []int64
|
|
follows map[int64]bool
|
|
seasonIDs []int64
|
|
bangumis map[string]*bangumi.Card
|
|
)
|
|
var (
|
|
seasonNum int
|
|
movieNum int
|
|
)
|
|
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, plat, seasonNum, movieNum, s.upUserNum, s.uvLimit, s.userNum, s.userVideoLimit, s.biliUserNum, s.biliUserVideoLimit, rid, highlight, build, pn, ps, now)
|
|
if err != nil {
|
|
log.Error("%+v", err)
|
|
return
|
|
}
|
|
if code == model.ForbidCode || code == model.NoResultCode {
|
|
res = _emptyResult
|
|
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)
|
|
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 {
|
|
aids = append(aids, vr.Aid)
|
|
}
|
|
case search.TypeVideo:
|
|
aids = append(aids, v.Video.ID)
|
|
case search.TypeMediaBangumi, search.TypeMediaFt:
|
|
seasonIDs = append(seasonIDs, v.Media.SeasonID)
|
|
}
|
|
}
|
|
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(aids) != 0 {
|
|
g.Go(func() (err error) {
|
|
if am, err = s.arcDao.Archives(ctx, aids); err != nil {
|
|
log.Error("%+v", err)
|
|
err = nil
|
|
}
|
|
return
|
|
})
|
|
}
|
|
if len(seasonIDs) != 0 {
|
|
g.Go(func() (err error) {
|
|
if bangumis, err = s.bgmDao.Card(ctx, mid, seasonIDs); err != nil {
|
|
log.Error("%+v", 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}
|
|
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:
|
|
i.FromVideo(v.Video, am[v.Video.ID])
|
|
case search.TypeMediaBangumi:
|
|
i.FromMedia(v.Media, "", model.GotoBangumi, bangumis)
|
|
case search.TypeMediaFt:
|
|
i.FromMedia(v.Media, "", model.GotoMovie, bangumis)
|
|
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, am)
|
|
case search.TypeBiliUser:
|
|
if follows[v.User.Mid] {
|
|
i.Attentions = 1
|
|
}
|
|
i.FromUpUser(v.User, am)
|
|
case search.TypeSpecialS:
|
|
i.FromOperate(v.Operate, model.GotoSpecialS)
|
|
case search.TypeQuery:
|
|
i.Title = v.TypeName
|
|
i.FromQuery(v.Query)
|
|
case search.TypeConverge:
|
|
var (
|
|
avids, artids []int64
|
|
avm map[int64]*api.Arc
|
|
artm map[int64]*article.Meta
|
|
)
|
|
for _, c := range v.Operate.ContentList {
|
|
switch c.Type {
|
|
case 0:
|
|
avids = append(avids, c.ID)
|
|
case 2:
|
|
artids = append(artids, c.ID)
|
|
}
|
|
}
|
|
g, ctx := errgroup.WithContext(c)
|
|
if len(aids) != 0 {
|
|
g.Go(func() (err error) {
|
|
if avm, err = s.arcDao.Archives(ctx, avids); 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, avm, artm)
|
|
case search.TypeTwitter:
|
|
i.FromTwitter(v.Twitter)
|
|
}
|
|
if i.Goto != "" {
|
|
item = append(item, i)
|
|
}
|
|
}
|
|
res.Item = item
|
|
if all.EggInfo != nil {
|
|
res.EasterEgg = &search.EasterEgg{ID: all.EggInfo.Source, ShowCount: all.EggInfo.ShowCount}
|
|
}
|
|
return
|
|
}
|
|
// archive
|
|
for _, v := range all.Result.Video {
|
|
aids = append(aids, v.ID)
|
|
}
|
|
if duration == "0" && order == "totalrank" && rid == 0 {
|
|
for _, v := range all.Result.Movie {
|
|
if v.Type == "movie" {
|
|
aids = append(aids, v.Aid)
|
|
}
|
|
}
|
|
}
|
|
if pn == 1 {
|
|
for _, v := range all.Result.User {
|
|
for _, vr := range v.Res {
|
|
aids = append(aids, vr.Aid)
|
|
}
|
|
}
|
|
for _, v := range all.Result.BiliUser {
|
|
for _, vr := range v.Res {
|
|
aids = append(aids, vr.Aid)
|
|
}
|
|
owners = append(owners, v.Mid)
|
|
}
|
|
}
|
|
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(aids) != 0 {
|
|
g.Go(func() (err error) {
|
|
if am, err = s.arcDao.Archives(ctx, aids); err != nil {
|
|
log.Error("%+v", err)
|
|
err = nil
|
|
}
|
|
return
|
|
})
|
|
}
|
|
if err = g.Wait(); err != nil {
|
|
log.Error("%+v", err)
|
|
return
|
|
}
|
|
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, now time.Time) (res *search.TypeSearch, err error) {
|
|
switch sType {
|
|
case "upper":
|
|
if res, err = s.upper(c, mid, keyword, mobiApp, device, platform, buvid, filtered, order, s.biliUserVideoLimit, highlight, build, userType, orderSort, 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 res, err = s.srchDao.Season2(c, mid, keyword, mobiApp, device, platform, buvid, highlight, build, pn, ps); err != nil {
|
|
return
|
|
}
|
|
case "movie2":
|
|
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
|
|
}
|
|
}
|
|
if res == nil {
|
|
res = &search.TypeSearch{Items: []*search.Item{}}
|
|
}
|
|
return
|
|
}
|
|
|
|
// Suggest3 for search suggest
|
|
func (s *Service) Suggest3(c context.Context, mid int64, platform, buvid, keyword string, build, highlight int, mobiApp string, now time.Time) (res *search.SuggestionResult3) {
|
|
var (
|
|
suggest *search.Suggest3
|
|
err error
|
|
aids []int64
|
|
am map[int64]*api.Arc
|
|
)
|
|
res = &search.SuggestionResult3{}
|
|
if suggest, err = s.srchDao.Suggest3(c, mid, platform, buvid, keyword, 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 {
|
|
aids = append(aids, v.Ref)
|
|
}
|
|
}
|
|
}
|
|
g, ctx := errgroup.WithContext(c)
|
|
if len(aids) != 0 {
|
|
g.Go(func() (err error) {
|
|
if am, err = s.arcDao.Archives(ctx, aids); 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, am)
|
|
res.List = append(res.List, si)
|
|
}
|
|
res.TrackID = suggest.TrackID
|
|
return
|
|
}
|
|
|
|
// convertNav deal with old search pageinfo to new.
|
|
func (s *Service) convertNav(all *search.Search, plat int8, build int, lang string) (nis []*search.NavInfo) {
|
|
const (
|
|
_showHide = 0
|
|
)
|
|
var (
|
|
season = "番剧"
|
|
upper = "用户"
|
|
movie = "影视"
|
|
article = "专栏"
|
|
)
|
|
if lang == model.Hant {
|
|
season = "番劇"
|
|
upper = "UP主"
|
|
movie = "影視"
|
|
article = "專欄"
|
|
}
|
|
nis = make([]*search.NavInfo, 0, 4)
|
|
// season
|
|
// media season
|
|
if 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)
|
|
}
|
|
// upper
|
|
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)
|
|
}
|
|
// media movie
|
|
if 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 all.PageInfo.Article != nil {
|
|
var nav = &search.NavInfo{
|
|
Name: article,
|
|
Total: all.PageInfo.Article.NumResults,
|
|
Pages: all.PageInfo.Article.Pages,
|
|
Type: 6,
|
|
}
|
|
nis = append(nis, nav)
|
|
}
|
|
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, now time.Time) (res *search.TypeSearch, err error) {
|
|
var (
|
|
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, 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 {
|
|
return
|
|
}
|
|
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
|
|
}
|
|
|
|
// 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 []*tag.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
|
|
}
|