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 }