488 lines
15 KiB
Go
488 lines
15 KiB
Go
package searchser
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
"videoweb/app/api/proto"
|
|
"videoweb/app/service/configser"
|
|
"videoweb/pub/common/log"
|
|
"videoweb/pub/common/netutil"
|
|
"videoweb/pub/common/srvdef"
|
|
"videoweb/pub/models/contenttagmod"
|
|
"videoweb/pub/models/esmod"
|
|
"videoweb/pub/models/playletmod"
|
|
"videoweb/pub/models/playletthememod"
|
|
"videoweb/pub/models/videoinformationmod"
|
|
"videoweb/pub/pubdef"
|
|
|
|
"github.com/patrickmn/go-cache"
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/mongo/options"
|
|
)
|
|
|
|
var (
|
|
Cache *cache.Cache
|
|
)
|
|
|
|
func init() {
|
|
Cache = cache.New(5*time.Minute, 5*time.Minute)
|
|
}
|
|
func GetHotSearchItem(ctx context.Context) []*proto.HotSearchItem {
|
|
cacheKey := fmt.Sprintf("mdnet_hotsearchitem")
|
|
var resp []*proto.HotSearchItem
|
|
if cacheData, exists := Cache.Get(cacheKey); exists {
|
|
if item, ok := cacheData.([]*proto.HotSearchItem); ok {
|
|
resp = item
|
|
}
|
|
}
|
|
if resp == nil || len(resp) == 0 {
|
|
opts := &options.FindOptions{}
|
|
opts.SetSort(bson.M{
|
|
videoinformationmod.FN.VideoInformation.Sort: -1,
|
|
})
|
|
opts.SetLimit(15)
|
|
list, _ := videoinformationmod.FindManyVideoInformations(ctx, bson.M{
|
|
videoinformationmod.FN.VideoInformation.AddedTime: bson.M{
|
|
"$gte": time.Now().AddDate(0, 0, -7),
|
|
},
|
|
videoinformationmod.FN.VideoInformation.Status: pubdef.InformationStatusVerifyOK,
|
|
}, opts)
|
|
data := make([]*proto.HotSearchItem, 0)
|
|
if len(list) > 0 {
|
|
for _, item := range list {
|
|
data = append(data, &proto.HotSearchItem{
|
|
Id: item.Id,
|
|
Title: item.Title,
|
|
HotValue: item.Real.WatchTimes * 100,
|
|
})
|
|
}
|
|
}
|
|
resp = data
|
|
Cache.Set(cacheKey, data, 5*time.Minute)
|
|
}
|
|
return resp
|
|
}
|
|
|
|
// Search 查询搜索结果
|
|
func Search(ctx context.Context, title string, mediaType int64, page int64, sort string) *proto.SearchResp {
|
|
resp := &proto.SearchResp{
|
|
TotalSize: 0,
|
|
Sort: make([]proto.SearchSortItem, 0),
|
|
SearchRecommend: make([]string, 0),
|
|
Media: make([]proto.VideoInfoData, 0),
|
|
Playlets: make([]proto.PlayletData, 0),
|
|
PageNum: page,
|
|
PageSize: pubdef.PageSize,
|
|
}
|
|
resp.InTitle = title
|
|
resp.InType = mediaType
|
|
resp.Sort = getSearchSort()
|
|
resp.SearchRecommend = getSearchRecommend(title)
|
|
resp.Config = configser.GetRequestConfig(ctx)
|
|
var err error
|
|
if len(title) > 0 {
|
|
if mediaType < 0 {
|
|
searchAll(ctx, resp, title, page, sort)
|
|
} else if mediaType == 0 {
|
|
resp.TotalSize, resp.Media, err = searchVideo(ctx, title, page, sort)
|
|
if err != nil {
|
|
log.Warn("Search searchVideo Failed", log.Any("mediaType", mediaType), log.Any("title", title), log.E(err))
|
|
return resp
|
|
}
|
|
} else if mediaType == 6 {
|
|
resp.TotalSize, resp.Playlets, err = searchPlaylet(ctx, title, page, sort)
|
|
}
|
|
}
|
|
resp.TotalSize = pubdef.GetTotalPage(resp.TotalSize, pubdef.PageSize)
|
|
return resp
|
|
}
|
|
|
|
// searchAll 并发搜索全部资源类型
|
|
func searchAll(ctx context.Context, resp *proto.SearchResp, title string, page int64, sort string) {
|
|
var wg sync.WaitGroup
|
|
var mu sync.Mutex
|
|
var (
|
|
mediaList []proto.VideoInfoData
|
|
playletList []proto.PlayletData
|
|
)
|
|
addTotal := func(total int64) {
|
|
mu.Lock()
|
|
resp.TotalSize += total
|
|
mu.Unlock()
|
|
}
|
|
wg.Add(2)
|
|
go func() {
|
|
defer wg.Done()
|
|
total, data, err := searchVideo(ctx, title, page, sort)
|
|
if err != nil {
|
|
log.Warn("SearchAll searchVideo Failed", log.Any("title", title), log.E(err))
|
|
return
|
|
}
|
|
mu.Lock()
|
|
mediaList = data
|
|
mu.Unlock()
|
|
addTotal(total)
|
|
}()
|
|
go func() {
|
|
defer wg.Done()
|
|
total, data, err := searchPlaylet(ctx, title, page, sort)
|
|
if err != nil {
|
|
log.Warn("SearchAll searchPlaylet Failed", log.Any("title", title), log.E(err))
|
|
return
|
|
}
|
|
mu.Lock()
|
|
playletList = data
|
|
mu.Unlock()
|
|
addTotal(total)
|
|
}()
|
|
wg.Wait()
|
|
resp.Media = mediaList
|
|
resp.Playlets = playletList
|
|
}
|
|
|
|
// searchVideo 搜索视频数据
|
|
func searchVideo(ctx context.Context, title string, page int64, sort string) (int64, []proto.VideoInfoData, error) {
|
|
data := make([]proto.VideoInfoData, 0)
|
|
total := int64(0)
|
|
skip := (page - 1) * pubdef.PageSize
|
|
total, list, err := esmod.SearchVideo(title, skip, pubdef.PageSize, sort, false)
|
|
if err != nil {
|
|
log.Warn("Search SearchVideo", log.Any("title", title), log.Any("sort", sort), log.E(err))
|
|
return total, data, err
|
|
}
|
|
if list != nil && len(list) > 0 {
|
|
config := configser.GetRequestConfig(ctx)
|
|
ids := make([]int64, len(list))
|
|
for i, v := range list {
|
|
ids[i] = v.Id
|
|
}
|
|
medias, err := videoinformationmod.FindManyVideoInformations(ctx, bson.M{
|
|
videoinformationmod.FN.VideoInformation.Id: bson.M{"$in": ids},
|
|
videoinformationmod.FN.VideoInformation.Status: pubdef.InformationStatusVerifyOK,
|
|
}, options.Find().SetProjection(searchVideoProjection()))
|
|
if err != nil {
|
|
log.Warn("Search FindManyVideoInformations", log.Any("title", title), log.E(err))
|
|
return total, data, err
|
|
}
|
|
mediaMap := make(map[int64]*videoinformationmod.VideoInformation, len(medias))
|
|
for _, media := range medias {
|
|
mediaMap[media.Id] = media
|
|
}
|
|
for _, id := range ids {
|
|
media := mediaMap[id]
|
|
if media == nil {
|
|
continue
|
|
}
|
|
info := proto.VideoInfoToData(media, config.ImgCdn, configser.GetCreateInfo(media.CreatorId, config.ImgCdn))
|
|
for _, tagId := range media.Tags {
|
|
tagInfo := configser.GetVideoTag(tagId)
|
|
if tagInfo != nil {
|
|
info.Tags = append(info.Tags, proto.TagsInfoData{
|
|
Id: tagInfo.Id,
|
|
Name: tagInfo.Name,
|
|
Slug: tagInfo.Slug,
|
|
})
|
|
}
|
|
}
|
|
data = append(data, info)
|
|
}
|
|
|
|
}
|
|
return total, data, nil
|
|
}
|
|
|
|
// searchPlaylet 搜索短剧数据
|
|
func searchPlaylet(ctx context.Context, title string, page int64, sort string) (int64, []proto.PlayletData, error) {
|
|
data := make([]proto.PlayletData, 0)
|
|
total := int64(0)
|
|
skip := (page - 1) * pubdef.PageSize
|
|
total, list, err := esmod.SearchPlaylet(title, skip, pubdef.PageSize, sort)
|
|
if err != nil {
|
|
log.Warn("Search SearchPlaylet", log.Any("title", title), log.Any("sort", sort), log.E(err))
|
|
return total, data, err
|
|
}
|
|
if len(list) == 0 {
|
|
return total, data, nil
|
|
}
|
|
config := configser.GetRequestConfig(ctx)
|
|
ids := make([]int64, 0, len(list))
|
|
for _, item := range list {
|
|
if item != nil && item.Id > 0 {
|
|
ids = append(ids, item.Id)
|
|
}
|
|
}
|
|
if len(ids) == 0 {
|
|
return total, data, nil
|
|
}
|
|
playlets, err := playletmod.FindManyPlaylet(ctx, bson.M{
|
|
playletmod.FN.Playlet.Id: bson.M{"$in": ids},
|
|
playletmod.FN.Playlet.Status: playletmod.StatusUp,
|
|
}, options.Find().SetProjection(searchPlayletProjection()))
|
|
if err != nil {
|
|
log.Warn("Search FindManyPlaylet", log.Any("title", title), log.E(err))
|
|
return total, data, err
|
|
}
|
|
playletMap := make(map[int64]*playletmod.Playlet, len(playlets))
|
|
for _, item := range playlets {
|
|
if item != nil {
|
|
playletMap[item.Id] = item
|
|
}
|
|
}
|
|
tagNameMap, themeNameMap, err := buildSearchPlayletTagThemeNameMaps(ctx, playlets)
|
|
if err != nil {
|
|
return total, data, err
|
|
}
|
|
data = make([]proto.PlayletData, 0, len(ids))
|
|
for _, id := range ids {
|
|
item := playletMap[id]
|
|
if item == nil {
|
|
continue
|
|
}
|
|
data = append(data, searchPlayletToData(item, config.ImgCdn, tagNameMap, themeNameMap))
|
|
}
|
|
return total, data, nil
|
|
}
|
|
|
|
// searchVideoProjection 返回搜索视频列表需要的Mongo字段
|
|
func searchVideoProjection() bson.M {
|
|
return bson.M{
|
|
videoinformationmod.FN.VideoInformation.Id: 1,
|
|
videoinformationmod.FN.VideoInformation.MediaType: 1,
|
|
videoinformationmod.FN.VideoInformation.Title: 1,
|
|
videoinformationmod.FN.VideoInformation.Slug: 1,
|
|
videoinformationmod.FN.VideoInformation.Keyworld: 1,
|
|
videoinformationmod.FN.VideoInformation.AddedTime: 1,
|
|
videoinformationmod.FN.VideoInformation.CreatorId: 1,
|
|
videoinformationmod.FN.VideoInformation.CoverImg: 1,
|
|
videoinformationmod.FN.VideoInformation.Video.Cover: 1,
|
|
videoinformationmod.FN.VideoInformation.Video.Url: 1,
|
|
videoinformationmod.FN.VideoInformation.Video.H265Url: 1,
|
|
videoinformationmod.FN.VideoInformation.Video.VideoTime: 1,
|
|
videoinformationmod.FN.VideoInformation.IsHot: 1,
|
|
videoinformationmod.FN.VideoInformation.Real.ShowTimes: 1,
|
|
videoinformationmod.FN.VideoInformation.Real.WatchTimes: 1,
|
|
videoinformationmod.FN.VideoInformation.Desc: 1,
|
|
videoinformationmod.FN.VideoInformation.Bango: 1,
|
|
videoinformationmod.FN.VideoInformation.IssuedBrand: 1,
|
|
videoinformationmod.FN.VideoInformation.IssuedAt: 1,
|
|
videoinformationmod.FN.VideoInformation.UpdatedAt: 1,
|
|
videoinformationmod.FN.VideoInformation.PageUpdatedAt: 1,
|
|
videoinformationmod.FN.VideoInformation.Tags: 1,
|
|
}
|
|
}
|
|
|
|
// searchPlayletProjection 返回搜索短剧列表需要的Mongo字段
|
|
func searchPlayletProjection() bson.M {
|
|
return bson.M{
|
|
playletmod.FN.Playlet.Id: 1,
|
|
playletmod.FN.Playlet.Status: 1,
|
|
playletmod.FN.Playlet.IsRecommend: 1,
|
|
playletmod.FN.Playlet.IsFeatured: 1,
|
|
playletmod.FN.Playlet.IsOriginal: 1,
|
|
playletmod.FN.Playlet.ShowOriginal: 1,
|
|
playletmod.FN.Playlet.MediaCenterId: 1,
|
|
playletmod.FN.Playlet.OnlineAt: 1,
|
|
playletmod.FN.Playlet.MediaCreatedAt: 1,
|
|
playletmod.FN.Playlet.MediaUpdatedAt: 1,
|
|
playletmod.FN.Playlet.Title: 1,
|
|
playletmod.FN.Playlet.Slug: 1,
|
|
playletmod.FN.Playlet.Desc: 1,
|
|
playletmod.FN.Playlet.Anchor: 1,
|
|
playletmod.FN.Playlet.Cover: 1,
|
|
playletmod.FN.Playlet.CompressedCover: 1,
|
|
playletmod.FN.Playlet.Preview: 1,
|
|
playletmod.FN.Playlet.TagIds: 1,
|
|
playletmod.FN.Playlet.ThemeIds: 1,
|
|
playletmod.FN.Playlet.ContentType: 1,
|
|
playletmod.FN.Playlet.PayType: 1,
|
|
playletmod.FN.Playlet.SingleCoins: 1,
|
|
playletmod.FN.Playlet.TotalEpisode: 1,
|
|
playletmod.FN.Playlet.UpdateStatus: 1,
|
|
playletmod.FN.Playlet.CountBrowse: 1,
|
|
playletmod.FN.Playlet.VirtualCountBrowse: 1,
|
|
playletmod.FN.Playlet.HeatValue: 1,
|
|
playletmod.FN.Playlet.VirtualHeatValue: 1,
|
|
playletmod.FN.Playlet.CountCollect: 1,
|
|
playletmod.FN.Playlet.CountLike: 1,
|
|
playletmod.FN.Playlet.VirtualCountLike: 1,
|
|
playletmod.FN.Playlet.SeoInfo.Title: 1,
|
|
playletmod.FN.Playlet.SeoInfo.Description: 1,
|
|
playletmod.FN.Playlet.SeoInfo.Keyworld: 1,
|
|
playletmod.FN.Playlet.CreatedAt: 1,
|
|
playletmod.FN.Playlet.UpdatedAt: 1,
|
|
}
|
|
}
|
|
|
|
func searchPlayletToData(item *playletmod.Playlet, imgCdn string, tagNameMap contenttagmod.ContentTagMap, themeNameMap map[int64]string) proto.PlayletData {
|
|
if item == nil {
|
|
return proto.PlayletData{}
|
|
}
|
|
return proto.PlayletData{
|
|
Id: item.Id,
|
|
Status: item.Status,
|
|
IsRecommend: item.IsRecommend,
|
|
IsNew: searchPlayletIsNew(item.OnlineAt),
|
|
IsFeatured: item.IsFeatured,
|
|
IsOriginal: item.IsOriginal || item.MediaCenterId == 0,
|
|
ShowOriginal: item.ShowOriginal,
|
|
OnlineAt: item.OnlineAt,
|
|
MediaCreatedAt: item.MediaCreatedAt,
|
|
MediaUpdatedAt: item.MediaUpdatedAt,
|
|
Title: item.Title,
|
|
Slug: item.Slug,
|
|
Desc: item.Desc,
|
|
Anchor: item.Anchor,
|
|
Cover: netutil.MergeURL(imgCdn, item.DisplayCover()),
|
|
Preview: netutil.MergeURL(imgCdn, item.Preview),
|
|
TagIds: item.TagIds,
|
|
TagNames: searchPlayletTagNamesFromMap(item.TagIds, tagNameMap),
|
|
TagSlugs: searchPlayletTagSlugsFromMap(item.TagIds, tagNameMap),
|
|
ThemeIds: item.ThemeIds,
|
|
ThemeNames: searchPlayletNamesFromMap(item.ThemeIds, themeNameMap),
|
|
ContentType: item.ContentType,
|
|
PayType: item.PayType,
|
|
SingleCoins: item.SingleCoins,
|
|
TotalEpisode: item.TotalEpisode,
|
|
UpdateStatus: item.UpdateStatus,
|
|
CountBrowse: item.DisplayCountBrowse(),
|
|
HeatValue: item.DisplayHeatValue(),
|
|
CountCollect: item.CountCollect,
|
|
CountLike: item.CountLike,
|
|
VirtualCountLike: item.VirtualCountLike,
|
|
DisplayCountLike: item.CountLike + item.VirtualCountLike,
|
|
CreatedAt: item.CreatedAt,
|
|
UpdatedAt: item.UpdatedAt,
|
|
SeoInfo: item.SeoInfo,
|
|
}
|
|
}
|
|
|
|
func buildSearchPlayletTagThemeNameMaps(ctx context.Context, list playletmod.PlayletList) (contenttagmod.ContentTagMap, map[int64]string, *srvdef.SrvErr) {
|
|
tagIds := searchPlayletRelationIds(list, func(item *playletmod.Playlet) []int64 {
|
|
return item.TagIds
|
|
})
|
|
themeIds := searchPlayletRelationIds(list, func(item *playletmod.Playlet) []int64 {
|
|
return item.ThemeIds
|
|
})
|
|
tagNameMap, err := buildSearchPlayletTagNameMap(ctx, tagIds)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
themeNameMap, err := buildSearchPlayletThemeNameMap(ctx, themeIds)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return tagNameMap, themeNameMap, nil
|
|
}
|
|
|
|
func buildSearchPlayletTagNameMap(ctx context.Context, ids []int64) (contenttagmod.ContentTagMap, *srvdef.SrvErr) {
|
|
result := make(contenttagmod.ContentTagMap, len(ids))
|
|
if len(ids) == 0 {
|
|
return result, nil
|
|
}
|
|
return contenttagmod.GetMapTags(ctx, ids)
|
|
}
|
|
|
|
func searchPlayletTagNamesFromMap(ids []int64, tags contenttagmod.ContentTagMap) []string {
|
|
result := make([]string, 0, len(ids))
|
|
for _, id := range ids {
|
|
if tag := tags[id]; tag != nil && tag.Name != "" {
|
|
result = append(result, tag.Name)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func searchPlayletTagSlugsFromMap(ids []int64, tags contenttagmod.ContentTagMap) []string {
|
|
result := make([]string, 0, len(ids))
|
|
for _, id := range ids {
|
|
if tag := tags[id]; tag != nil && tag.Name != "" {
|
|
result = append(result, tag.Slug)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func buildSearchPlayletThemeNameMap(ctx context.Context, ids []int64) (map[int64]string, *srvdef.SrvErr) {
|
|
result := make(map[int64]string, len(ids))
|
|
if len(ids) == 0 {
|
|
return result, nil
|
|
}
|
|
list, err := playletthememod.FindManyPlayletTheme(ctx, bson.M{
|
|
playletthememod.FN.PlayletTheme.Id: bson.M{"$in": ids},
|
|
}, options.Find().SetProjection(bson.D{
|
|
{Key: playletthememod.FN.PlayletTheme.Id, Value: 1},
|
|
{Key: playletthememod.FN.PlayletTheme.Name, Value: 1},
|
|
}))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, item := range list {
|
|
if item != nil {
|
|
result[item.Id] = item.Name
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func searchPlayletRelationIds(list playletmod.PlayletList, pick func(item *playletmod.Playlet) []int64) []int64 {
|
|
result := make([]int64, 0)
|
|
seen := make(map[int64]struct{})
|
|
for _, item := range list {
|
|
if item == nil {
|
|
continue
|
|
}
|
|
for _, id := range pick(item) {
|
|
if id <= 0 {
|
|
continue
|
|
}
|
|
if _, ok := seen[id]; ok {
|
|
continue
|
|
}
|
|
seen[id] = struct{}{}
|
|
result = append(result, id)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func searchPlayletNamesFromMap(ids []int64, nameMap map[int64]string) []string {
|
|
result := make([]string, 0, len(ids))
|
|
for _, id := range ids {
|
|
if name := nameMap[id]; name != "" {
|
|
result = append(result, name)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func searchPlayletIsNew(onlineAt time.Time) bool {
|
|
if onlineAt.IsZero() {
|
|
return false
|
|
}
|
|
return onlineAt.After(time.Now().AddDate(0, 0, -7))
|
|
}
|
|
|
|
// getSearchRecommend 返回搜索推荐词
|
|
func getSearchRecommend(title string) []string {
|
|
return make([]string, 0)
|
|
}
|
|
|
|
func getSearchSort() []proto.SearchSortItem {
|
|
resp := make([]proto.SearchSortItem, 0)
|
|
resp = append(resp, proto.SearchSortItem{
|
|
Key: string(proto.SearchSortTypeDefault),
|
|
Title: "相关度",
|
|
})
|
|
resp = append(resp, proto.SearchSortItem{
|
|
Key: string(proto.SearchSortTypeHot),
|
|
Title: "热度最高",
|
|
})
|
|
resp = append(resp, proto.SearchSortItem{
|
|
Key: string(proto.SearchSortTypeNew),
|
|
Title: "最新发布",
|
|
})
|
|
return resp
|
|
}
|