package vidser import ( "91porn-server/app/appg" "91porn-server/app/service/vidhelpser" "91porn-server/common" "91porn-server/common/constant" "91porn-server/common/constant/redisconst" "91porn-server/common/elastic" "91porn-server/common/log" "91porn-server/models/commod" "91porn-server/models/v/mediamod" "91porn-server/models/v/mediatagmod" "91porn-server/models/v/tagmod" "91porn-server/models/v/vidmod" "fmt" "math/rand" "sync" "time" "github.com/vmihailenco/msgpack/v5" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo/options" ) func NewsTypeToSting(in string) string { var libraryTypeName string switch in { case constant.SP: libraryTypeName = "影片" case constant.SHORT: libraryTypeName = "抖音" case constant.Cartoon: libraryTypeName = "动漫" case constant.Comics: libraryTypeName = "漫画" case constant.PIC: libraryTypeName = "图集" case constant.COVER: libraryTypeName = "帖子" } return libraryTypeName } // GetLibrary 获取片库详情 func GetLibrary(uid uint64) (res vidmod.LibraryData, err error) { var data vidmod.LibraryData // 获取缓存 str, err := appg.Redis.Get(redisconst.VideoLibraryCache) if err != nil { log.Warn(fmt.Sprintf("uid:%v;缓存获取片库信息异常:%v", uid, err)) } if str != nil { if err = msgpack.Unmarshal([]byte(*str), &data); err == nil { return data, nil } log.Warn(fmt.Sprintf("uid:%v;解析片库缓存数据异常:%v", uid, err)) } data.OrderBy = append(data.OrderBy, vidmod.SortKey{ Key: "new", Name: "最新上架", }, vidmod.SortKey{ Key: "playNum", Name: "最多观看", }, vidmod.SortKey{ Key: "love", Name: "最多收藏", }) data.Canvas = append(data.Canvas, vidmod.SortKey{ Key: constant.SP, Name: NewsTypeToSting(constant.SP), }, vidmod.SortKey{ Key: constant.SHORT, Name: NewsTypeToSting(constant.SHORT), }, vidmod.SortKey{ Key: constant.Cartoon, Name: NewsTypeToSting(constant.Cartoon), }, vidmod.SortKey{ Key: constant.Comics, Name: NewsTypeToSting(constant.Comics), }, vidmod.SortKey{ Key: constant.PIC, Name: NewsTypeToSting(constant.PIC), }, vidmod.SortKey{ Key: constant.COVER, Name: NewsTypeToSting(constant.COVER), }) data.PaymentType = append(data.PaymentType, vidmod.SortKey{ Key: "", Name: "全部", }, vidmod.SortKey{ Key: "vip", Name: "VIP", }, vidmod.SortKey{ Key: "point", Name: "金币", }) data.TimeType = append( data.TimeType, vidmod.SortKey{ Key: "1", Name: "本月", }, vidmod.SortKey{ Key: "2", Name: "三个月内", }, vidmod.SortKey{ Key: "3", Name: "半年内", }, vidmod.SortKey{ Key: "4", Name: "更久", }) data.VidTags = append(data.VidTags, vidmod.Tag{ ID: "", Name: "全部类型", }) data.ACGTags = append(data.ACGTags, vidmod.Tag{ ID: "", Name: "全部类型", }) wg := sync.WaitGroup{} wg.Add(2) common.Go(func() { defer wg.Done() // 获取最热标签 tags, _, err := tagmod.GetLibraryTagList(commod.Page{PageNumber: 1, PageSize: 20}) if err != nil { return } if len(tags) > 0 { for _, t := range tags { data.VidTags = append(data.VidTags, vidmod.Tag{ ID: t.ID.Hex(), Name: t.TagName, }) } } }) common.Go(func() { defer wg.Done() // 获取ACG所有推荐标签 tagFilter := bson.M{"active": true, "isDelete": false, "isDiscovery": true} tagOp := options.Find().SetLimit(20).SetSort(bson.D{{Key: "sort", Value: -1}}) mediaTags, err := mediatagmod.QueryAllList(tagFilter, tagOp) if err != nil { return } if len(mediaTags) > 0 { for _, t := range mediaTags { data.ACGTags = append(data.ACGTags, vidmod.Tag{ ID: t.ID.Hex(), Name: t.Name, }) } } }) wg.Wait() common.Go(func() { // 加入缓存 随机1-3分钟缓存 random := rand.Intn(120) + 60 d, err := msgpack.Marshal(data) if err != nil { return } if err = appg.Redis.Set(redisconst.VideoLibraryCache, d, time.Duration(random)*time.Second); err != nil { log.Warn(fmt.Sprintf("uid:%v, 保存缓存数据异常:%v", uid, err)) } }) return data, nil } type LibraryElasticSearchRequest struct { Keyword LibraryReq `json:"keyword" bson:"keyword" binding:"required"` // 关键词 UID uint64 `json:"-"` // 用户ID commod.Page } type LibraryElasticSearchResponse struct { List []vidmod.ESVideo `json:"list"` // list HasNext bool `json:"hasNext"` // hasNext Total int `json:"total"` // total } type LibraryReq struct { Canvas SortKey `json:"canvas" bson:"canvas"` // 视频分类 OrderBy SortKey `json:"orderBy" bson:"orderBy"` // 视频排序 Tags SearchTag `json:"tags" bson:"tags"` // 全部标签 PaymentType SortKey `json:"paymentType" bson:"paymentType"` // 付费分类 TimeType SortKey `json:"timeType" bson:"timeType"` // 时间排序 } type LibraryData struct { Canvas []SortKey `json:"canvas" bson:"canvas"` // 视频分类 OrderBy []SortKey `json:"orderBy" bson:"orderBy"` // 视频排序 VidTags []SearchTag `json:"vidTags" bson:"vidTags"` // 全部视频标签 ACGTags []SearchTag `json:"acgTags" bson:"acgTags"` // 全部ACG标签 PaymentType []SortKey `json:"paymentType" bson:"paymentType"` // 付费分类 TimeType []SortKey `json:"timeType" bson:"timeType"` // 时间排序 } type SortKey struct { Key string `json:"key" bson:"key"` // 健值 Name string `json:"name" bson:"name"` // 健名称 } type SearchTag struct { ID string `json:"id" bson:"id"` // 标签ID Name string `json:"name" bson:"name"` // 标签名称 } type AppElasticSearchLibraryResponse struct { List []*vidmod.VideoInfoResp `json:"list" bson:"list"` // 视频列表 AllMediaList []*mediamod.AppMediaBase `json:"allMediaList" bson:"allMediaList"` // ACG列表 HasNext bool `json:"hasNext" bson:"hasNext"` // 是否下一页 Total int `json:"total" bson:"total"` // 总数 } func (in *LibraryElasticSearchRequest) CheckSearchType() (AppElasticSearchLibraryResponse, error) { var ( data AppElasticSearchLibraryResponse err error ) switch in.Keyword.Canvas.Key { case constant.SP, constant.SHORT, constant.PIC, constant.COVER: data, err = in.VidLibrarySearch() case constant.Cartoon, constant.Comics: data, err = in.MediaLibrarySearch() } if err != nil { return data, err } return data, nil } func (in *LibraryElasticSearchRequest) VidLibrarySearch() (AppElasticSearchLibraryResponse, error) { resp := AppElasticSearchLibraryResponse{} from := in.Skip64() size := in.Limit64() must := elastic.A{ {"term": elastic.M{"newsType.keyword": in.Keyword.Canvas.Key}}, } if in.Keyword.Tags.ID != "" { tag := elastic.M{"term": elastic.M{"tags.keyword": in.Keyword.Tags.ID}} must = append(must, tag) } timeType := HandleTimeType(in.Keyword.TimeType.Key) must = append(must, timeType...) payType := HandlePayType(in.Keyword.PaymentType.Key) must = append(must, payType...) short := HandleShort(in.Keyword.OrderBy.Key) // 根据数据结构组装数据 query := elastic.M{ "query": elastic.M{ "bool": elastic.M{ "must": must, }, }, "sort": short, "from": from, "size": size + 1, } res, err := vidmod.SearchByCondWithTotal(query) if err != nil { return resp, nil } hasNext := false videosIDs := make([]primitive.ObjectID, 0) for _, v := range res.Hits { videosIDs = append(videosIDs, v.Source.ID) } if len(videosIDs) > int(size) { hasNext = true videosIDs = videosIDs[:size] } vidMods, err := vidmod.GetVideoListByIDs(videosIDs) if err != nil { return resp, nil } var ( sortVidModels = make([]*vidmod.VideoModel, 0) vidMap = make(map[primitive.ObjectID]*vidmod.VideoModel) ) for _, videoModel := range vidMods { vidMap[videoModel.ID] = videoModel } for _, vid := range videosIDs { if v, ok := vidMap[vid]; ok { sortVidModels = append(sortVidModels, v) } } data := vidhelpser.NewEncodeVideoInfoNotStatusForSearch(in.UID, sortVidModels) resp.List = data resp.HasNext = hasNext resp.Total = res.Total.Value // 查询结果数量出现不一致,说名有的帖子不存在了,需要删除 if len(videosIDs) > 0 && len(videosIDs) != len(vidMods) { common.Go(func() { HandleDiffVideo(vidMap, videosIDs) }) } return resp, nil } func HandleDiffVideo(vidMods map[primitive.ObjectID]*vidmod.VideoModel, videosIDs []primitive.ObjectID) { var source = elastic.M{} var diffVid []string for _, vid := range videosIDs { if _, ok := vidMods[vid]; !ok { source[vid.Hex()] = vid.Hex() diffVid = append(diffVid, vid.Hex()) } } if len(source) > 0 { log.Info(fmt.Sprintf("search delete videosIDs:%v", diffVid)) if err := vidmod.DeleteByCond(source); err != nil { log.Info(fmt.Sprintf("Elastic del data failed, err:%v", err)) return } log.Info(fmt.Sprintf("Elastic del data succsss, count:%v", len(source))) } } func HandleTimeType(key string) elastic.A { var timeTypeValue = elastic.A{} now := time.Now() switch key { case "1": // 本月 timeTypeValue = append(timeTypeValue, elastic.M{"range": elastic.M{"reviewAt": elastic.M{"gt": now.AddDate(0, -1, 0)}}}, ) case "2": // 3月内 timeTypeValue = append(timeTypeValue, elastic.M{"range": elastic.M{"reviewAt": elastic.M{"gt": now.AddDate(0, -3, 0), "lt": now.AddDate(0, -1, 0)}}}, ) case "3": // 半年内 timeTypeValue = append(timeTypeValue, elastic.M{"range": elastic.M{"reviewAt": elastic.M{"gt": now.AddDate(0, -6, 0), "lt": now.AddDate(0, -3, 0)}}}, ) case "4": // 更久 timeTypeValue = append(timeTypeValue, elastic.M{"range": elastic.M{"reviewAt": elastic.M{"gt": now.AddDate(-5, 0, 0), "lt": now.AddDate(0, -6, 0)}}}, ) default: timeTypeValue = append(timeTypeValue, elastic.M{"range": elastic.M{"reviewAt": elastic.M{"gt": now.AddDate(-3, -2, 0)}}}, ) } return timeTypeValue } func HandlePayType(key string) elastic.A { var payTypeValue = elastic.A{} switch key { case "free": payTypeValue = append(payTypeValue, elastic.M{"term": elastic.M{"status": 3}}, elastic.M{"term": elastic.M{"coins": 0}}, ) case "vip": payTypeValue = append(payTypeValue, elastic.M{"term": elastic.M{"status": 1}}, elastic.M{"term": elastic.M{"coins": 0}}, ) case "point": payTypeValue = append(payTypeValue, elastic.M{"terms": elastic.M{"status": []int{1, 3}}}, elastic.M{"range": elastic.M{"coins": elastic.M{"gt": 0}}}, ) default: payTypeValue = append(payTypeValue, elastic.M{"terms": elastic.M{"status": []int{1, 3}}}) } return payTypeValue } func HandleShort(key string) elastic.A { var payTypeValue elastic.A switch key { case "playNum": payTypeValue = elastic.A{{"playCount": elastic.M{"order": "desc"}}} case "love": payTypeValue = elastic.A{{"collectCount": elastic.M{"order": "desc"}}} case "new": payTypeValue = elastic.A{{"reviewAt": elastic.M{"order": "desc"}}} default: payTypeValue = elastic.A{{"fakeLikeCount": elastic.M{"order": "desc"}}, {"reviewAt": elastic.M{"order": "desc"}}} } return payTypeValue }