410 lines
11 KiB
Go
410 lines
11 KiB
Go
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
|
|
}
|