Initial commit: web_hghl_server
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
package searchser
|
||||
@@ -0,0 +1,487 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user