@@ -0,0 +1,76 @@
|
||||
package audiobooksearcher
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"91porn-server/app/appg"
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/models/v/audiobookmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// FictionSearcher FictionSearcher
|
||||
type FictionSearcher struct {
|
||||
uid *uint64
|
||||
}
|
||||
|
||||
// NewFictionSearcher NewFictionSearcher
|
||||
func NewFictionSearcher(uid *uint64) searcher.Searcher {
|
||||
return &FictionSearcher{
|
||||
uid: uid,
|
||||
}
|
||||
}
|
||||
|
||||
// Search Search
|
||||
func (t *FictionSearcher) Search(keywrod searcher.KeyWorder, opt searcher.Opter) (result searcher.Resulter, err error) {
|
||||
kw, ok := keywrod.Get().(string)
|
||||
if !ok {
|
||||
return nil, errors.New("keyword type error")
|
||||
}
|
||||
if kw == "" { //关键字为空,热搜
|
||||
audiobook, hasNext, err := audiobookmod.GetNew(opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Result{dat: audiobook, hasNext: hasNext}, nil
|
||||
}
|
||||
if appg.Conf.Elastic.IsActive {
|
||||
audiobook, _ := esSearch(kw, opt)
|
||||
hasNext := false
|
||||
if len(audiobook) > int(opt.Limit()) {
|
||||
hasNext = true
|
||||
audiobook = audiobook[:opt.Limit()]
|
||||
}
|
||||
ab, err := fillAudioBook(*t.uid, audiobook)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Result{dat: ab, hasNext: hasNext}, nil
|
||||
}
|
||||
audiobook, hasNext, err := audiobookmod.GetNew(opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Result{dat: audiobook, hasNext: hasNext}, nil
|
||||
}
|
||||
|
||||
func fillAudioBook(uid uint64, data []audiobookmod.AudioBookBase) (res []audiobookmod.AudioBookAppRes, err error) {
|
||||
ids := make([]primitive.ObjectID, len(data))
|
||||
for i := range data {
|
||||
ids[i] = data[i].ID
|
||||
}
|
||||
return audiobookmod.GetByIds(ids)
|
||||
}
|
||||
|
||||
func esSearch(keyword string, opt searcher.Opter) (data []audiobookmod.AudioBookBase, err error) {
|
||||
audiobook, err := audiobookmod.Search(keyword, opt.Skip(), opt.Limit()+1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
data = make([]audiobookmod.AudioBookBase, len(audiobook))
|
||||
for i := range audiobook {
|
||||
data[i] = audiobook[i].Source
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package audiobooksearcher
|
||||
|
||||
import (
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/models/v/audiobookmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type ObjectID = primitive.ObjectID
|
||||
|
||||
type Result struct {
|
||||
dat []audiobookmod.AudioBookAppRes
|
||||
hasNext bool
|
||||
}
|
||||
|
||||
func (*Result) Types() searcher.ResultTypes {
|
||||
return searcher.LouFengSearchResult
|
||||
}
|
||||
|
||||
func (r *Result) Data() interface{} {
|
||||
return r.dat
|
||||
}
|
||||
|
||||
func (r *Result) Count() int64 {
|
||||
return int64(len(r.dat))
|
||||
}
|
||||
|
||||
func (r *Result) HasNext() bool {
|
||||
return r.hasNext
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package fictionsearcher
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"91porn-server/app/appg"
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/models/v/fictionmod"
|
||||
)
|
||||
|
||||
// FictionSearcher FictionSearcher
|
||||
type FictionSearcher struct {
|
||||
uid *uint64
|
||||
}
|
||||
|
||||
// NewFictionSearcher NewFictionSearcher
|
||||
func NewFictionSearcher(uid *uint64) searcher.Searcher {
|
||||
return &FictionSearcher{
|
||||
uid: uid,
|
||||
}
|
||||
}
|
||||
|
||||
// Search Search
|
||||
func (t *FictionSearcher) Search(keywrod searcher.KeyWorder, opt searcher.Opter) (result searcher.Resulter, err error) {
|
||||
var fiction []fictionmod.FictionBase
|
||||
kw, ok := keywrod.Get().(string)
|
||||
if !ok {
|
||||
return nil, errors.New("keyword type error")
|
||||
}
|
||||
hasNext := false
|
||||
if kw == "" { //关键字为空,热搜
|
||||
fiction, hasNext, err = fictionmod.GetNew(opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Result{fiction, hasNext}, nil
|
||||
}
|
||||
if appg.Conf.Elastic.IsActive {
|
||||
fiction, err = esSearch(kw, opt)
|
||||
if len(fiction) > int(opt.Limit()) {
|
||||
hasNext = true
|
||||
fiction = fiction[:opt.Limit()]
|
||||
}
|
||||
} else {
|
||||
fiction, hasNext, err = fictionmod.GetNew(opt)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Result{fiction, hasNext}, nil
|
||||
}
|
||||
|
||||
func esSearch(keyword string, opt searcher.Opter) (data []fictionmod.FictionBase, err error) {
|
||||
fiction, err := fictionmod.Search(keyword, opt.Skip(), opt.Limit()+1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
data = make([]fictionmod.FictionBase, len(fiction))
|
||||
for i := range fiction {
|
||||
data[i] = fiction[i].Source
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package fictionsearcher
|
||||
|
||||
import (
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/models/v/fictionmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type ObjectID = primitive.ObjectID
|
||||
|
||||
type Result struct {
|
||||
dat []fictionmod.FictionBase
|
||||
hasNext bool
|
||||
}
|
||||
|
||||
func (*Result) Types() searcher.ResultTypes {
|
||||
return searcher.LouFengSearchResult
|
||||
}
|
||||
|
||||
func (r *Result) Data() interface{} {
|
||||
return r.dat
|
||||
}
|
||||
|
||||
func (r *Result) Count() int64 {
|
||||
return int64(len(r.dat))
|
||||
}
|
||||
|
||||
func (r *Result) HasNext() bool {
|
||||
return r.hasNext
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package searcher
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/common/timeutil/timerange"
|
||||
"91porn-server/models/v/tonerecomod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// KeyWorder KeyWorder
|
||||
type KeyWorder interface {
|
||||
Get() interface{}
|
||||
}
|
||||
|
||||
// Keyword Keyword
|
||||
type Keyword struct {
|
||||
Word string
|
||||
}
|
||||
|
||||
func (s *Keyword) Get() interface{} {
|
||||
return s.Word
|
||||
}
|
||||
|
||||
// StringList StringList
|
||||
type StringList struct {
|
||||
Words []string
|
||||
}
|
||||
|
||||
func (s *StringList) Get() interface{} {
|
||||
return s.Words
|
||||
}
|
||||
|
||||
// ObjectList ObjectList
|
||||
type ObjIDList struct {
|
||||
IDList []primitive.ObjectID
|
||||
}
|
||||
|
||||
func (o *ObjIDList) Get() interface{} {
|
||||
return o.IDList
|
||||
}
|
||||
|
||||
// ObjectList ObjectList
|
||||
type ObjID struct {
|
||||
ID primitive.ObjectID
|
||||
}
|
||||
|
||||
func (o *ObjID) Get() interface{} {
|
||||
return o.ID
|
||||
}
|
||||
|
||||
// ThemeList ThemeList
|
||||
type ThemeList struct {
|
||||
Themes []tonerecomod.ThemeType
|
||||
}
|
||||
|
||||
func (s *ThemeList) Get() interface{} {
|
||||
return s.Themes
|
||||
}
|
||||
|
||||
// TimeRange TimeRange
|
||||
type TimeRange struct {
|
||||
Range timerange.TimeRange
|
||||
}
|
||||
|
||||
func (t *TimeRange) Get() interface{} {
|
||||
return t.Range
|
||||
}
|
||||
|
||||
func NewTimeRange(start, end time.Time) KeyWorder {
|
||||
timeRange := timerange.TimeRange{
|
||||
Head: start,
|
||||
Tail: end,
|
||||
}
|
||||
return &TimeRange{timeRange}
|
||||
}
|
||||
|
||||
// SumDate SumDate
|
||||
type SumDate struct {
|
||||
Date time.Time
|
||||
}
|
||||
|
||||
func (t *SumDate) Get() interface{} {
|
||||
return t.Date
|
||||
}
|
||||
|
||||
func NewSumDate(position time.Time) KeyWorder {
|
||||
sumDate := timerange.LocDayRange(position).Head
|
||||
return &SumDate{sumDate}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package searcher
|
||||
|
||||
import (
|
||||
"91porn-server/models/v/locmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// LocInfo LocInfo
|
||||
type LocInfo struct {
|
||||
ID *primitive.ObjectID `json:"id" bson:"_id"`
|
||||
Longitude *string `json:"longitude,omitempty"` //经度
|
||||
Latitude *string `json:"latitude,omitempty"` //纬度
|
||||
Country *string `json:"country,omitempty"` //国家
|
||||
CountryCode *string `json:"countryCode,omitempty"` //国家编码
|
||||
Province *string `json:"province,omitempty"` //省份
|
||||
ProvinceCode *string `json:"provinceCode,omitempty"` //省份编码
|
||||
City *string `json:"city,omitempty"` //城市
|
||||
CityCode *string `json:"cityCode,omitempty"` //城市编码
|
||||
Address *string `json:"address,omitempty"` //地址
|
||||
Cover *string `json:"cover,omitempty"` //封面
|
||||
Visit *int `json:"visit,omitempty"` //访问人数
|
||||
}
|
||||
|
||||
// LocMap LocMap
|
||||
func LocMap(lidList []ObjectID) (map[ObjectID]*LocInfo, error) {
|
||||
locMap, err := locmod.GetLocationInfoByIDs(lidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := make(map[ObjectID]*LocInfo, len(locMap))
|
||||
for k, v := range locMap {
|
||||
loc := v
|
||||
lInfo := LocInfo{
|
||||
ID: &loc.ID,
|
||||
Longitude: &loc.Longitude,
|
||||
Latitude: &loc.Latitude,
|
||||
Country: &loc.Country,
|
||||
CountryCode: &loc.CountryCode,
|
||||
Province: &loc.Province,
|
||||
ProvinceCode: &loc.ProvinceCode,
|
||||
City: &loc.City,
|
||||
CityCode: &loc.CityCode,
|
||||
Address: &loc.Address,
|
||||
Cover: &loc.Cover,
|
||||
Visit: &loc.Visit,
|
||||
}
|
||||
m[k] = &lInfo
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package searcher
|
||||
|
||||
import (
|
||||
"91porn-server/app/service/walletser"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/v/usermod"
|
||||
)
|
||||
|
||||
// UserInfo UserInfo
|
||||
type UserInfo struct {
|
||||
UID *uint64 `json:"uid,omitempty"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Age *int `json:"age,omitempty"`
|
||||
Gender *string `json:"gender,omitempty"` //性别
|
||||
Portrait *string `json:"portrait,omitempty"` //头像
|
||||
Region *string `json:"region,omitempty"` //地区
|
||||
Summary *string `json:"summary,omitempty"` //简介
|
||||
VipLevel *int `json:"vipLevel,omitempty"` //vip等级
|
||||
RechargeLevel *int `json:"rechargeLevel" ` //头像显示的vip等级,累计充值决定
|
||||
SuperUser *bool `json:"superUser"` //大v
|
||||
ActiveValue *int `json:"activeValue"` //活跃度
|
||||
OfficialCert *bool `json:"officialCert"` //是否官方认证
|
||||
}
|
||||
|
||||
// UserRes UserRes
|
||||
type UserRes struct {
|
||||
UserInfo `bson:",inline"`
|
||||
HasFollowed *bool `json:"hasFollowed,omitempty"` //已关注
|
||||
FansCount *int64 `json:"fansCount,omitempty"` //粉丝数
|
||||
}
|
||||
|
||||
// MapToUserResList MapToUserResList
|
||||
func MapToUserResList(userList []*usermod.User, uid uint64) []UserRes {
|
||||
uidList := make([]uint64, len(userList))
|
||||
for i, u := range userList {
|
||||
uidList[i] = u.UID
|
||||
}
|
||||
mLevel, err := walletser.GetUidsRchgLevel(uidList)
|
||||
if err != nil {
|
||||
log.Error("GetUidsRchgLevel error", log.E(err))
|
||||
return []UserRes{}
|
||||
}
|
||||
userResList := make([]UserRes, len(userList))
|
||||
for i, userp := range userList {
|
||||
age := userp.Age()
|
||||
level := mLevel[userp.UID].Level
|
||||
userInfo := UserInfo{
|
||||
Age: &age,
|
||||
UID: &userp.UID,
|
||||
Name: &userp.Name,
|
||||
Gender: &userp.Gender,
|
||||
Portrait: &userp.Portrait,
|
||||
Region: &userp.Region,
|
||||
Summary: &userp.Summary,
|
||||
VipLevel: &userp.VipLevel,
|
||||
SuperUser: &userp.SuperUser,
|
||||
ActiveValue: &userp.ActiveValue,
|
||||
OfficialCert: &userp.OfficialCert,
|
||||
RechargeLevel: &level,
|
||||
}
|
||||
userResList[i] = UserRes{
|
||||
UserInfo: userInfo,
|
||||
}
|
||||
}
|
||||
return userResList
|
||||
}
|
||||
|
||||
// UserResMap UserResMap
|
||||
func UserResMap(uidList []uint64, uid uint64) (map[uint64]*UserRes, error) {
|
||||
userList, err := usermod.FindUsersByUID(uidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userResList := MapToUserResList(userList, uid)
|
||||
m := make(map[uint64]*UserRes, len(userList))
|
||||
for _, res := range userResList {
|
||||
if res.UID != nil {
|
||||
_res := res
|
||||
m[*res.UID] = &_res
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package searcher
|
||||
|
||||
// Opter Opter
|
||||
type Opter interface {
|
||||
SetSkip(v int64) Opter
|
||||
Skip() int64
|
||||
SetLimit(v int64) Opter
|
||||
Limit() int64
|
||||
SetSort(key string, v int64) Opter
|
||||
Sort() D
|
||||
}
|
||||
|
||||
type Option struct {
|
||||
skip *int64
|
||||
limit *int64
|
||||
sort D
|
||||
}
|
||||
|
||||
const MaxLimit = 50
|
||||
|
||||
const MinLimit = 1
|
||||
|
||||
func (o *Option) Skip() int64 {
|
||||
if o.skip == nil {
|
||||
return 0
|
||||
}
|
||||
return *o.skip
|
||||
}
|
||||
|
||||
func (o *Option) SetSkip(v int64) Opter {
|
||||
o.skip = &v
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *Option) Limit() int64 {
|
||||
if o.limit == nil {
|
||||
return MinLimit
|
||||
}
|
||||
if *o.limit > MaxLimit {
|
||||
return MaxLimit
|
||||
}
|
||||
return *o.limit
|
||||
}
|
||||
|
||||
func (o *Option) SetLimit(v int64) Opter {
|
||||
o.limit = &v
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *Option) Sort() D {
|
||||
return o.sort
|
||||
}
|
||||
|
||||
func (o *Option) SetSort(key string, v int64) Opter {
|
||||
o.sort = append(o.sort, E{Key: key, Value: v})
|
||||
return o
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package searcher
|
||||
|
||||
type ResultTypes = int
|
||||
|
||||
const (
|
||||
TagSearchResult ResultTypes = 1 << iota //0 which is 00000001
|
||||
ToneSearchResult
|
||||
UserSearchResult
|
||||
VidPCountSearchResult
|
||||
VidRichSearchResult
|
||||
VidSearchResult
|
||||
VidTagSearchResult
|
||||
VidToneSearchResult
|
||||
VidHotKeywordSearchResult
|
||||
LouFengSearchResult
|
||||
)
|
||||
|
||||
type Resulter interface {
|
||||
Types() ResultTypes
|
||||
Data() interface{}
|
||||
Count() int64
|
||||
HasNext() bool
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package searcher
|
||||
|
||||
// Searcher Searcher
|
||||
type Searcher interface {
|
||||
Search(KeyWorder, Opter) (Resulter, error)
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package searcher
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"91porn-server/models/v/tagmod"
|
||||
)
|
||||
|
||||
// TagInfo TagInfo
|
||||
type TagInfo struct {
|
||||
ID *ObjectID `json:"id,omitempty"` //标签id
|
||||
Name *string `json:"name,omitempty"` //标签名字
|
||||
CoverImg *string `json:"coverImg,omitempty"` //封面图片
|
||||
Description *string `json:"description,omitempty"` //文字说明
|
||||
PlayCount *int64 `json:"playCount,omitempty"` //播放量
|
||||
VidCount *int64 `json:"vidCount,omitempty"` //帖子数量
|
||||
FollowCount *int64 `json:"followCount,omitempty"` //关注数量
|
||||
}
|
||||
|
||||
// ToTagMap ToTagMap
|
||||
func ToTagMap(tInfoList []TagInfo) map[ObjectID]TagInfo {
|
||||
m := make(map[ObjectID]TagInfo, len(tInfoList))
|
||||
for _, v := range tInfoList {
|
||||
if v.ID == nil {
|
||||
continue
|
||||
}
|
||||
m[*v.ID] = v
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// TagInfoListByTIDList TagInfoListByTIDList
|
||||
func TagInfoListByTIDList(uid *uint64, tidList []ObjectID) ([]TagInfo, error) {
|
||||
tMap, err := tagmod.TagMap(tidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tagList := make([]tagmod.Tag, len(tMap))
|
||||
i := 0
|
||||
for _, tag := range tMap {
|
||||
tagList[i] = tag
|
||||
i++
|
||||
}
|
||||
tInfoList, err := TagInfoList(uid, tagList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tInfoList, nil
|
||||
}
|
||||
|
||||
// TagInfoList TagInfoList
|
||||
//
|
||||
//uid:nil
|
||||
func TagInfoList(uid *uint64, tagList []tagmod.Tag) ([]TagInfo, error) {
|
||||
tInfoList := make([]*TagInfo, 0, len(tagList))
|
||||
for _, v := range tagList {
|
||||
_v := v
|
||||
tagName := strings.TrimSpace(_v.TagName)
|
||||
if tagName == "" { //临时解决话题搜索返回空tag
|
||||
continue
|
||||
}
|
||||
tInfo := &TagInfo{
|
||||
ID: &_v.ID,
|
||||
Name: &tagName,
|
||||
CoverImg: &_v.CoverImg,
|
||||
Description: &_v.Description,
|
||||
PlayCount: &_v.TPlayCount, //夏沫确认 前端播放量计算方法
|
||||
VidCount: &_v.VideoCount,
|
||||
FollowCount: &_v.CollCount,
|
||||
}
|
||||
if uid == nil {
|
||||
tInfoList = append(tInfoList, tInfo)
|
||||
continue
|
||||
}
|
||||
tInfoList = append(tInfoList, tInfo)
|
||||
}
|
||||
tagInfoList := make([]TagInfo, len(tagList))
|
||||
for i, tInfo := range tInfoList {
|
||||
if uid == nil {
|
||||
tagInfoList[i] = *tInfo
|
||||
continue
|
||||
}
|
||||
tagInfoList[i] = *tInfo
|
||||
}
|
||||
return tagInfoList, nil
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package tagsearcher
|
||||
|
||||
import (
|
||||
"91porn-server/app/service/searcher"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type ObjectID = primitive.ObjectID
|
||||
|
||||
type TagRes = searcher.TagInfo
|
||||
|
||||
type Result struct {
|
||||
dat []TagRes
|
||||
hasNext bool
|
||||
}
|
||||
|
||||
func (*Result) Types() searcher.ResultTypes {
|
||||
return searcher.TagSearchResult
|
||||
}
|
||||
|
||||
func (r *Result) Data() interface{} {
|
||||
return r.dat
|
||||
}
|
||||
|
||||
func (r *Result) Count() int64 {
|
||||
return int64(len(r.dat))
|
||||
}
|
||||
|
||||
func (r *Result) HasNext() bool {
|
||||
return r.hasNext
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package tagsearcher
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"91porn-server/app/appg"
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/models/v/tagmod"
|
||||
)
|
||||
|
||||
// TagSearcher TagSearcher
|
||||
type TagSearcher struct {
|
||||
uid *uint64
|
||||
}
|
||||
|
||||
// NewTagSearcher NewTagSearcher
|
||||
func NewTagSearcher(uid *uint64) searcher.Searcher {
|
||||
return &TagSearcher{
|
||||
uid: uid,
|
||||
}
|
||||
}
|
||||
|
||||
// Search Search
|
||||
func (t *TagSearcher) Search(keywrod searcher.KeyWorder, opt searcher.Opter) (result searcher.Resulter, err error) {
|
||||
kw, ok := keywrod.Get().(string)
|
||||
if !ok {
|
||||
return nil, errors.New("keyword type error")
|
||||
}
|
||||
limitEx1 := opt.Limit() + 1
|
||||
hasNext := false
|
||||
if kw == "" { //关键字为空,热搜
|
||||
tags, err := tagmod.TagListByFakePlayCount(opt.Skip(), limitEx1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(tags) > int(opt.Limit()) {
|
||||
hasNext = true
|
||||
tags = tags[:opt.Limit()]
|
||||
}
|
||||
tagResList, err := searcher.TagInfoList(t.uid, tags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Result{tagResList, hasNext}, nil
|
||||
}
|
||||
var tags []tagmod.Tag
|
||||
if appg.Conf.Elastic.IsActive {
|
||||
tags, err = esSearch(kw, opt)
|
||||
} else {
|
||||
tags, err = tagmod.GetTagListByKeyword(kw, opt.Sort(), opt.Skip(), limitEx1)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(tags) > int(opt.Limit()) {
|
||||
hasNext = true
|
||||
tags = tags[:opt.Limit()]
|
||||
}
|
||||
tagResList, err := searcher.TagInfoList(t.uid, tags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Result{tagResList, hasNext}, nil
|
||||
}
|
||||
|
||||
func esSearch(keyword string, opt searcher.Opter) ([]tagmod.Tag, error) {
|
||||
esTags, err := tagmod.Search(keyword, opt.Skip(), opt.Limit()+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(esTags) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
tagsID := make([]ObjectID, len(esTags))
|
||||
for i, v := range esTags {
|
||||
tagsID[i] = v.ID
|
||||
}
|
||||
tMap, err := tagmod.FindTagsMapByIDS(tagsID)
|
||||
if err != nil || tMap == nil {
|
||||
return nil, err
|
||||
}
|
||||
tags := make([]tagmod.Tag, len(tagsID))
|
||||
for i, v := range tagsID {
|
||||
tags[i] = tMap[v]
|
||||
}
|
||||
return tags, nil
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package tonesearcher
|
||||
|
||||
import "91porn-server/app/service/searcher"
|
||||
|
||||
type ToneRes struct {
|
||||
Name string `json:"name"` //主题名
|
||||
Types string `json:"types"` //类型
|
||||
Cover string `json:"cover"` //封面
|
||||
PlayCount int64 `json:"playCount"` //播放数
|
||||
PayCount int64 `json:"payCount"` //购买数
|
||||
Sort int `json:"-"` //排序
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
dat []ToneRes
|
||||
hasNext bool
|
||||
}
|
||||
|
||||
func (*Result) Types() searcher.ResultTypes {
|
||||
return searcher.ToneSearchResult
|
||||
}
|
||||
|
||||
func (r *Result) Data() interface{} {
|
||||
return r.dat
|
||||
}
|
||||
|
||||
func (r *Result) Count() int64 {
|
||||
return int64(len(r.dat))
|
||||
}
|
||||
|
||||
func (r *Result) HasNext() bool {
|
||||
return r.hasNext
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package tonesearcher
|
||||
|
||||
import (
|
||||
"91porn-server/app/appg"
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/common/constant/redisconst"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/v/tonemod"
|
||||
"91porn-server/models/v/tonerecomod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
// M M
|
||||
type M = bson.M
|
||||
|
||||
type ToneValue = tonemod.ToneValue
|
||||
|
||||
// ToneSearcher ToneSearcher
|
||||
type ToneSearcher struct {
|
||||
}
|
||||
|
||||
// NewToneSearcher NewToneSearcher
|
||||
func NewToneSearcher() searcher.Searcher {
|
||||
return &ToneSearcher{}
|
||||
}
|
||||
|
||||
// Search Search
|
||||
func (t *ToneSearcher) Search(unuse1 searcher.KeyWorder, unuse2 searcher.Opter) (result searcher.Resulter, err error) {
|
||||
enable := true
|
||||
enableMatch := tonemod.EnableMatch{Enable: &enable}
|
||||
max := int64(7)
|
||||
list, err := tonemod.List(bson.D{{Key: "sortKey", Value: 1}}, 0, max, enableMatch.New())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
toneValue := GetToneValue()
|
||||
toneResList := make([]ToneRes, len(list))
|
||||
for i, v := range list {
|
||||
toneRes := ToneRes{
|
||||
Name: v.Name,
|
||||
Types: v.Types,
|
||||
Cover: v.Cover,
|
||||
Sort: v.SortKey,
|
||||
}
|
||||
switch v.Types {
|
||||
case tonerecomod.OfficialRecom:
|
||||
toneRes.PlayCount = toneValue.OfficialRecomPlayCount
|
||||
case tonerecomod.GoldCoinArea:
|
||||
toneRes.PayCount = toneValue.GoldCoinAreaPayCount
|
||||
case tonerecomod.LatestUpload:
|
||||
toneRes.PlayCount = toneValue.LatestUploadPlayCount
|
||||
case tonerecomod.MostLikes:
|
||||
toneRes.PlayCount = toneValue.MostLikesPlayCount
|
||||
case tonerecomod.PlayAtMost:
|
||||
toneRes.PlayCount = toneValue.PlayAtMostPlayCount
|
||||
case tonerecomod.MostComments:
|
||||
toneRes.PlayCount = toneValue.MostCommentsPlayCount
|
||||
case tonerecomod.FreeArea:
|
||||
toneRes.PlayCount = toneValue.FreeAreaPlayCount
|
||||
}
|
||||
toneResList[i] = toneRes
|
||||
}
|
||||
return &Result{toneResList, false}, nil
|
||||
}
|
||||
|
||||
func GetToneValue() ToneValue {
|
||||
var toneValue ToneValue
|
||||
redisKey := redisconst.ToneKey()
|
||||
if err := appg.Redis.Scan(redisKey, &toneValue); err != nil {
|
||||
log.Warn("tone Run redis Scan error", log.E(err))
|
||||
}
|
||||
return toneValue
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package searcher
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// LouFengInfo LouFengInfo
|
||||
type LouFengInfo struct {
|
||||
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"`
|
||||
Title string `json:"title" bson:"title,omitempty"` //标题
|
||||
Number int `json:"number" bson:"number,omitempty"` //编号
|
||||
Quantity string `json:"quantity" bson:"quantity,omitempty"` //数量
|
||||
Age string `json:"age" bson:"age,omitempty"` //年龄
|
||||
Cover string `json:"cover" bson:"cover,omitempty"` //封面
|
||||
Price string `json:"price" bson:"price"` //价格 单位 元
|
||||
BusinessHours string `json:"businessHours" bson:"businessHours,omitempty"` //营业时间 0-24
|
||||
ServiceItems []string `json:"serviceItems" bson:"serviceItems,omitempty"` //服务项目
|
||||
City string `json:"city" bson:"city,omitempty"` //市
|
||||
District string `json:"district" bson:"district,omitempty"` //区,县
|
||||
Contact string `json:"contact" bson:"contact,omitempty"` //联系方式
|
||||
ContactPrice int64 `json:"contactPrice" bson:"contactPrice,omitempty"` //联系方式价格 单位 金币(角)
|
||||
Impression string `json:"impression" bson:"impression,omitempty"` //狼友印象
|
||||
EnvStar int `json:"envStar" bson:"envStar,omitempty"` //环境评分
|
||||
PrettyStar int `json:"prettyStar" bson:"prettyStar,omitempty"` //颜值评分
|
||||
OverallRatings int `json:"overallRatings" bson:"overallRatings,omitempty"` //综合评分
|
||||
ServiceStar int `json:"serviceStar" bson:"serviceStar,omitempty"` //服务星级
|
||||
BusinessDate string `json:"businessDate" bson:"businessDate,omitempty"` //营业月 份 "2020-06"
|
||||
IsActive bool `json:"isActive " bson:"isActive"` //是否激活
|
||||
IsVerify bool `json:"isVerify" bson:"isVerify"` //是否验证
|
||||
CountPurchases int64 `json:"countPurchases" bson:"countPurchases"` //购买数
|
||||
CountBrowse int64 `json:"countBrowse" bson:"countBrowse"` //浏览数
|
||||
CountCollect int64 `json:"countCollect" bson:"countCollect"` //收藏数
|
||||
CreatedAt time.Time `json:"createdAt" bson:"createdAt,omitempty"` //创建时间
|
||||
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt,omitempty"` //修改时间
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package usersearcher
|
||||
|
||||
import "91porn-server/app/service/searcher"
|
||||
|
||||
type UserRes = searcher.UserRes
|
||||
|
||||
type Result struct {
|
||||
dat []searcher.UserRes
|
||||
hasNext bool
|
||||
}
|
||||
|
||||
func (*Result) Types() searcher.ResultTypes {
|
||||
return searcher.UserSearchResult
|
||||
}
|
||||
|
||||
func (r *Result) Data() interface{} {
|
||||
return r.dat
|
||||
}
|
||||
|
||||
func (r *Result) Count() int64 {
|
||||
return int64(len(r.dat))
|
||||
}
|
||||
|
||||
func (r *Result) HasNext() bool {
|
||||
return r.hasNext
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package usersearcher
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"91porn-server/app/appg"
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/models/v/usermod"
|
||||
)
|
||||
|
||||
// UserSearcher UserSearcher
|
||||
type UserSearcher struct {
|
||||
uid uint64
|
||||
}
|
||||
|
||||
// NewUserSearcher NewUserSearcher
|
||||
func NewUserSearcher(uid uint64) searcher.Searcher {
|
||||
return &UserSearcher{
|
||||
uid: uid,
|
||||
}
|
||||
}
|
||||
|
||||
// Search Search
|
||||
func (u *UserSearcher) Search(keyword searcher.KeyWorder, opt searcher.Opter) (result searcher.Resulter, err error) {
|
||||
kw, ok := keyword.Get().(string)
|
||||
if !ok {
|
||||
return nil, errors.New("keyword type error")
|
||||
}
|
||||
limitEx1 := opt.Limit() + 1
|
||||
hasNext := false
|
||||
if kw == "" { //关键字为空 返回最新用户
|
||||
uList, err := usermod.NewUserList(opt.Skip(), limitEx1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(uList) > int(opt.Limit()) {
|
||||
hasNext = true
|
||||
uList = uList[:opt.Limit()]
|
||||
}
|
||||
userResList := searcher.MapToUserResList(uList, u.uid)
|
||||
return &Result{userResList, hasNext}, nil
|
||||
}
|
||||
userList := make([]*usermod.User, 0, limitEx1)
|
||||
uid, err := strconv.ParseUint(kw, 10, 64)
|
||||
var esHasNext bool
|
||||
if err == nil { //能解析成Int 通过uid搜索
|
||||
user, err := usermod.FindUserByUID(uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if user != nil {
|
||||
userList = append(userList, user)
|
||||
}
|
||||
} else { //通过用户名搜索
|
||||
var uList []*usermod.User
|
||||
if appg.Conf.Elastic.IsActive {
|
||||
uList, esHasNext, err = esSearch(kw, opt)
|
||||
} else {
|
||||
uList, err = usermod.FindUsersByKeyword(kw, opt.Skip(), limitEx1)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userList = append(userList, uList...)
|
||||
}
|
||||
if len(userList) == 0 {
|
||||
return &Result{[]UserRes{}, false}, nil
|
||||
}
|
||||
if len(userList) > int(opt.Limit()) {
|
||||
hasNext = true
|
||||
userList = userList[:opt.Limit()]
|
||||
}
|
||||
if appg.Conf.Elastic.IsActive {
|
||||
hasNext = esHasNext
|
||||
}
|
||||
userResList := searcher.MapToUserResList(userList, u.uid)
|
||||
return &Result{userResList, hasNext}, nil
|
||||
}
|
||||
|
||||
func esSearch(keyword string, opt searcher.Opter) (users []*usermod.User, hasNext bool, err error) {
|
||||
esUsers, err := usermod.Search(keyword, opt.Skip(), opt.Limit()+1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(esUsers) == 0 {
|
||||
return
|
||||
}
|
||||
uidList := make([]uint64, len(esUsers))
|
||||
for i, v := range esUsers {
|
||||
uidList[i] = v.Source.UID
|
||||
}
|
||||
hasNext = len(esUsers) > int(opt.Limit())
|
||||
uMap, err := usermod.FindUsersMapByUID(uidList)
|
||||
if err != nil || uMap == nil {
|
||||
return
|
||||
}
|
||||
users = make([]*usermod.User, 0, len(esUsers))
|
||||
for _, v := range esUsers {
|
||||
if u, ok := uMap[v.Source.UID]; ok {
|
||||
users = append(users, u)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,562 @@
|
||||
package searcher
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"91porn-server/app/service/searchaccessser"
|
||||
"91porn-server/app/service/vidhelpser"
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/l/payvidlgmod"
|
||||
"91porn-server/models/v/cmtmod"
|
||||
"91porn-server/models/v/freeVidmod"
|
||||
"91porn-server/models/v/moduleconfmod"
|
||||
"91porn-server/models/v/productmod"
|
||||
"91porn-server/models/v/tagmod"
|
||||
"91porn-server/models/v/usermod"
|
||||
"91porn-server/models/v/videodiscountmod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// A A
|
||||
type A = bson.A
|
||||
|
||||
// M M
|
||||
type M = bson.M
|
||||
|
||||
// D D
|
||||
type D = bson.D
|
||||
|
||||
// E E
|
||||
type E = bson.E
|
||||
|
||||
// ObjectID ObjectID
|
||||
type ObjectID = primitive.ObjectID
|
||||
|
||||
// VideoInfo VideoInfo
|
||||
type VideoInfo struct {
|
||||
ID *ObjectID `json:"id" bson:"_id"`
|
||||
NewsType *string `json:"newsType"` //帖子类型, VID,视频帖子,COVER
|
||||
Title *string `json:"title,omitempty"` //视频标题
|
||||
Tags []TagInfo `json:"tags"` //视频标签
|
||||
SourceID *string `json:"sourceID,omitempty"` //视频在仓库中的资源ID
|
||||
SourceURL *string `json:"sourceURL,omitempty"` //视频资源地址Path
|
||||
H265Url *string `json:"h265Url"` // H.265 视频资源地址
|
||||
PlayTime *uint `json:"playTime,omitempty"` //影片长度
|
||||
Cover *string `json:"cover,omitempty"` //封面大图
|
||||
CoverThumb *string `json:"coverThumb,omitempty"` //封⾯小图
|
||||
SeriesCover []string `json:"seriesCover"` //封面套图
|
||||
Via *string `json:"via,omitempty"` //来源 自拍,上传
|
||||
PlayCount *int `json:"playCount,omitempty"` //总的播放量量(假数据)
|
||||
LikeCount *int `json:"likeCount,omitempty"` //点赞数(假数据)
|
||||
PayCount *int `json:"payCount,omitempty"` //购买数
|
||||
CommentCount *int `json:"commentCount,omitempty"` //评论数(假数据)
|
||||
ShareCount *int `json:"shareCount,omitempty"` //分享数(假数据)
|
||||
Coins *int64 `json:"coins,omitempty"` //折扣后价格
|
||||
Size *int `json:"size,omitempty"` //文件大小 byte
|
||||
Status *int `json:"status,omitempty"` //状态,0 未审核 1通过 2审核失败 3视为免费 默认为0
|
||||
Resolution *string `json:"resolution,omitempty"` //分辨率
|
||||
Ratio *float64 `json:"ratio,omitempty"` //宽高比
|
||||
MimeType *string `json:"mimeType,omitempty"` //视频格式类型
|
||||
Actor *string `json:"actor,omitempty"`
|
||||
FreeTime *int `json:"freeTime,omitempty"` //免费观影时长
|
||||
CreatedAt *time.Time `json:"createdAt,omitempty"` //创建时间
|
||||
ReviewedAt *time.Time `json:"reviewAt,omitempty"` // 审核时间
|
||||
UInfo vidmod.Publisher `json:"publisher,omitempty" bson:"publisher"` //用户信息
|
||||
FreeArea *bool `json:"freeArea,omitempty"` //免费专区
|
||||
PublisherID *uint64 `json:"-"` //发布者ID(user)
|
||||
LocationID *primitive.ObjectID `json:"-"` //位置ID
|
||||
IsTopping *bool `json:"isTopping"`
|
||||
IsRecommend *bool `json:"isRecommend"` //力荐
|
||||
IsChoosen *bool `json:"isChoosen"` //置精
|
||||
OriginCoins *int64 `json:"originCoins"` // 视频原价
|
||||
SearchAccessToken *string `json:"searchAccessToken,omitempty"`
|
||||
ShowFreeTrialBadge bool `json:"showFreeTrialBadge" bson:"-"`
|
||||
FreeTrialRemaining uint64 `json:"freeTrialRemaining" bson:"-"`
|
||||
CanUseFreeTrial bool `json:"canUseFreeTrial" bson:"-"`
|
||||
}
|
||||
|
||||
// VideoMap VideoMap
|
||||
func videoMap(vidList []ObjectID, uid uint64, allowSearchOnly bool) (map[ObjectID]*VideoInfo, error) {
|
||||
if len(vidList) == 0 {
|
||||
return make(map[ObjectID]*VideoInfo), nil
|
||||
}
|
||||
videoMap, vInfo, err := vidmod.SearchVideoMap(vidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
blockedModules := make(map[string]struct{})
|
||||
if allowSearchOnly {
|
||||
excludedModules, excludeErr := moduleconfmod.ExcludedSearchModuleIDs()
|
||||
if excludeErr != nil {
|
||||
return nil, excludeErr
|
||||
}
|
||||
for _, moduleID := range excludedModules {
|
||||
blockedModules[moduleID] = struct{}{}
|
||||
}
|
||||
} else {
|
||||
moduleIDs := make([]string, 0, len(vInfo))
|
||||
for _, video := range vInfo {
|
||||
if video.MID != "" {
|
||||
moduleIDs = append(moduleIDs, video.MID)
|
||||
}
|
||||
}
|
||||
blockedModules, err = moduleconfmod.BlockedOutsideSearchModuleIDs(moduleIDs, time.Now())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var (
|
||||
vidInfoMap = make(map[ObjectID]*VideoInfo, len(videoMap))
|
||||
tidUniqueMap = make(map[ObjectID]byte)
|
||||
mFree = map[uint64]bool{}
|
||||
self *usermod.User
|
||||
videoDiscLog videodiscountmod.VideoDiscountLog
|
||||
largestDiscount int
|
||||
wg sync.WaitGroup
|
||||
mUser map[uint64]*vidmod.Publisher
|
||||
)
|
||||
uids := make([]uint64, len(vInfo))
|
||||
for i, v := range vInfo {
|
||||
uids[i] = v.PublisherID
|
||||
}
|
||||
wg.Add(5)
|
||||
common.Go(func() {
|
||||
defer wg.Done()
|
||||
self, err = usermod.FindUserByUID(uid)
|
||||
if err != nil {
|
||||
log.Error("VideoMap: usermod.FindUserByUID", log.Any("uid", uid), log.E(err))
|
||||
return
|
||||
}
|
||||
})
|
||||
common.Go(func() {
|
||||
defer wg.Done()
|
||||
mUser = vidhelpser.GetUserInfo2Map(uid, uids)
|
||||
})
|
||||
common.Go(func() {
|
||||
defer wg.Done()
|
||||
largestDiscount, err = productmod.GetLargestDiscountVIPCard()
|
||||
if err != nil {
|
||||
log.Error("VideoMap GetLargestDiscountVIPCard", log.Any("uid", uid), log.E(err))
|
||||
}
|
||||
})
|
||||
common.Go(func() {
|
||||
defer wg.Done()
|
||||
mFree = freeVidmod.GetFreeVideoMapByUID(uid)
|
||||
})
|
||||
common.Go(func() {
|
||||
defer wg.Done()
|
||||
videoDiscLog, err = videodiscountmod.GetByUID(uid)
|
||||
if err != nil {
|
||||
log.Error("VideoMap videodiscountmod.GetByUID", log.Any("uid", uid), log.E(err))
|
||||
}
|
||||
})
|
||||
wg.Wait()
|
||||
freeTrialContext := vidhelpser.FreeTrialBadgeContextForUser(uid, self)
|
||||
for vid, vidInfo := range videoMap {
|
||||
videoPtr := vidInfo
|
||||
if _, blocked := blockedModules[videoPtr.MID]; blocked {
|
||||
continue
|
||||
}
|
||||
status := videoPtr.Status
|
||||
if status == 3 {
|
||||
status = 1
|
||||
}
|
||||
var uInfo vidmod.Publisher
|
||||
if mUser[vidInfo.PublisherID] != nil {
|
||||
uInfo = *mUser[vidInfo.PublisherID]
|
||||
} else {
|
||||
//处理作者被删除的情况
|
||||
if len(mUser) > 0 {
|
||||
for i := range mUser {
|
||||
uInfo = *mUser[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
newsType := vidmod.SP
|
||||
if len(videoPtr.NewsType) != 0 {
|
||||
newsType = videoPtr.NewsType
|
||||
}
|
||||
discountCoins := vidhelpser.CalcVideoCoins(videoPtr.Coins, videoPtr.PublisherID, self, mFree, videoDiscLog,
|
||||
largestDiscount, nil)
|
||||
h265URL := vidhelpser.H265URLForApp(videoPtr)
|
||||
vInfo := VideoInfo{
|
||||
ID: &videoPtr.ID,
|
||||
NewsType: &newsType,
|
||||
Title: &videoPtr.Title, //视频标题
|
||||
Tags: []TagInfo{}, //视频标签
|
||||
SourceID: &videoPtr.SourceID, //视频在仓库中的资源ID
|
||||
SourceURL: &videoPtr.SourceURL, //视频资源地址Path
|
||||
H265Url: &h265URL, // H.265 视频资源地址
|
||||
PlayTime: &videoPtr.PlayTime, //影片长度
|
||||
Cover: &videoPtr.Cover, //封面大图
|
||||
CoverThumb: &videoPtr.CoverThumb, //封⾯小图
|
||||
SeriesCover: videoPtr.SeriesCover,
|
||||
Via: &videoPtr.Via, //来源 自拍,上传
|
||||
PlayCount: &videoPtr.FakePlayCount, //总的播放量量(假数据)
|
||||
LikeCount: &videoPtr.FakeLikeCount, //点赞数(假数据)
|
||||
CommentCount: &videoPtr.FakeCommentCount, //评论数(假数据)
|
||||
ShareCount: &videoPtr.FakeShareCount, //分享数(假数据)
|
||||
Size: &videoPtr.Size, //文件大小 byte
|
||||
Ratio: &videoPtr.Ratio, //分辨率
|
||||
Status: &status, //状态,0 未审核 1通过 2审核失败 3视为免费 默认为0
|
||||
Resolution: &videoPtr.Resolution, //分辨率
|
||||
MimeType: &videoPtr.MimeType, //视频格式类型
|
||||
Actor: &videoPtr.Actor,
|
||||
FreeTime: &videoPtr.FreeTime, //免费观影时长
|
||||
PublisherID: &videoPtr.PublisherID, //发布者ID(user)
|
||||
FreeArea: &videoPtr.FreeArea, //免费专区
|
||||
LocationID: &videoPtr.Location, //位置ID
|
||||
CreatedAt: &videoPtr.CreatedAt, //创建时间
|
||||
ReviewedAt: &videoPtr.ReviewAt,
|
||||
UInfo: uInfo,
|
||||
IsTopping: &videoPtr.IsTopping,
|
||||
IsChoosen: &videoPtr.IsChoosen,
|
||||
IsRecommend: &videoPtr.IsRecommend,
|
||||
OriginCoins: &videoPtr.Coins, // 定价
|
||||
Coins: &discountCoins, // 折扣价
|
||||
}
|
||||
vInfo.ShowFreeTrialBadge, vInfo.FreeTrialRemaining, vInfo.CanUseFreeTrial = freeTrialContext.Fields(
|
||||
newsType,
|
||||
videoPtr.Coins,
|
||||
videoPtr.FreeArea,
|
||||
videoPtr.PublisherID,
|
||||
)
|
||||
if allowSearchOnly {
|
||||
token, tokenErr := searchaccessser.Issue(uid, videoPtr.ID.Hex(), time.Now())
|
||||
if tokenErr != nil {
|
||||
return nil, tokenErr
|
||||
}
|
||||
vInfo.SearchAccessToken = &token
|
||||
}
|
||||
if len(videoPtr.Tags) != 0 {
|
||||
for _, tid := range videoPtr.Tags {
|
||||
tidUniqueMap[tid] = 0
|
||||
}
|
||||
}
|
||||
//fill video Map
|
||||
vidInfoMap[vid] = &vInfo
|
||||
}
|
||||
tidList := make([]ObjectID, len(tidUniqueMap))
|
||||
i := 0
|
||||
for k := range tidUniqueMap {
|
||||
tidList[i] = k
|
||||
i++
|
||||
}
|
||||
//获取所有tagInfo List
|
||||
tInfoList, err := TagInfoListByTIDList(&uid, tidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tInfoMap := ToTagMap(tInfoList)
|
||||
//vidInfoMap填充tagInfo
|
||||
for vid, vidInfo := range vidInfoMap {
|
||||
video := videoMap[vid]
|
||||
tidList := video.Tags
|
||||
tInfoList := make([]TagInfo, 0, len(tidList))
|
||||
for _, t := range video.Tags {
|
||||
tInfo := tInfoMap[t]
|
||||
tInfoList = append(tInfoList, tInfo)
|
||||
}
|
||||
vidInfo.Tags = tInfoList
|
||||
}
|
||||
return vidInfoMap, nil
|
||||
}
|
||||
|
||||
// VidStatus VidStatus
|
||||
type VidStatus struct {
|
||||
HasPaid *bool `json:"hasPaid,omitempty"` //已支付
|
||||
}
|
||||
|
||||
// VidAndStatus VidAndStatus
|
||||
type VidAndStatus struct {
|
||||
VID ObjectID
|
||||
*VidStatus
|
||||
}
|
||||
|
||||
// VidStatusMap VidStatusMap
|
||||
func VidStatusMap(vidList []ObjectID, uid uint64) map[ObjectID]VidStatus {
|
||||
var (
|
||||
payUniqList = make([]string, 0, len(vidList))
|
||||
payUniqMap = make(map[ObjectID]string, len(vidList))
|
||||
)
|
||||
for _, v := range vidList {
|
||||
payUniq := payvidlgmod.Unique(uid, v)
|
||||
payUniqList = append(payUniqList, payUniq)
|
||||
payUniqMap[v] = payUniq
|
||||
}
|
||||
payStatueMap, err := payvidlgmod.PayStatueMap(payUniqList)
|
||||
if err != nil {
|
||||
return map[ObjectID]VidStatus{}
|
||||
}
|
||||
vidStatusMap := make(map[ObjectID]VidStatus, len(vidList))
|
||||
for _, v := range vidList {
|
||||
payHash := payUniqMap[v]
|
||||
payStatue := payStatueMap[payHash]
|
||||
vidStatusMap[v] = VidStatus{
|
||||
HasPaid: &payStatue,
|
||||
}
|
||||
}
|
||||
return vidStatusMap
|
||||
}
|
||||
|
||||
// Publisher Publisher
|
||||
type Publisher = UserRes
|
||||
|
||||
// VideoRes VideoRes
|
||||
type VideoRes struct {
|
||||
VideoInfo `bson:",inline"` //视屏
|
||||
LocInfo `json:"location" bson:"location"` //位置
|
||||
VidStatus `json:"vidStatus" bson:"vidStatus"` //用户与视频状态信息
|
||||
Publisher `json:"publisher" bson:"publisher"` //发布者
|
||||
}
|
||||
|
||||
// GetVideoRes GetVideoRes
|
||||
func GetVideoRes(vid ObjectID, vInfoMap map[ObjectID]*VideoInfo, publMap map[uint64]*UserRes, locMap map[ObjectID]*LocInfo, statusMap map[ObjectID]VidStatus) VideoRes {
|
||||
res := VideoRes{}
|
||||
video := vInfoMap[vid]
|
||||
if video == nil {
|
||||
return res
|
||||
}
|
||||
res.VideoInfo = *video
|
||||
if video.PublisherID != nil {
|
||||
publisher := publMap[*video.PublisherID]
|
||||
if publisher != nil {
|
||||
res.Publisher = *publisher
|
||||
}
|
||||
}
|
||||
if video.LocationID != nil {
|
||||
loc := locMap[*video.LocationID]
|
||||
if loc != nil {
|
||||
res.LocInfo = *loc
|
||||
}
|
||||
}
|
||||
status := statusMap[vid]
|
||||
res.VidStatus = status
|
||||
return res
|
||||
}
|
||||
|
||||
func getVideoParentCommentCountMap(vidList []ObjectID) map[ObjectID]int {
|
||||
res := make(map[ObjectID]int, len(vidList))
|
||||
if len(vidList) == 0 {
|
||||
return res
|
||||
}
|
||||
countMap, err := cmtmod.CmtFindParentCountByObjIDs(cmtmod.CmtTypeVideo, vidList)
|
||||
if err != nil {
|
||||
log.Error("GetVideoResList CmtFindParentCountByObjIDs error", log.E(err), log.Any("vidList", vidList))
|
||||
return res
|
||||
}
|
||||
for id, count := range countMap {
|
||||
res[id] = int(count)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// GetVideoResList GetVideoResList
|
||||
func GetVideoResList(uid uint64, vidList []ObjectID) ([]VideoRes, error) {
|
||||
return getVideoResList(uid, vidList, false)
|
||||
}
|
||||
|
||||
// GetVideoResListForSearch 保留当前仅能通过搜索入口访问的视频。
|
||||
func GetVideoResListForSearch(uid uint64, vidList []ObjectID) ([]VideoRes, error) {
|
||||
return getVideoResList(uid, vidList, true)
|
||||
}
|
||||
|
||||
func getVideoResList(uid uint64, vidList []ObjectID, allowSearchOnly bool) ([]VideoRes, error) {
|
||||
videoCount := len(vidList)
|
||||
//vid->video Map
|
||||
videoMap, err := videoMap(vidList, uid, allowSearchOnly)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commentCountMap := getVideoParentCommentCountMap(vidList)
|
||||
//Publisher
|
||||
//发布者ID List
|
||||
pidList := make([]uint64, 0, videoCount)
|
||||
for _, video := range videoMap {
|
||||
if video.PublisherID == nil {
|
||||
continue
|
||||
}
|
||||
pidList = append(pidList, *video.PublisherID)
|
||||
}
|
||||
//LocationID List
|
||||
lidList := make([]ObjectID, 0, videoCount)
|
||||
for _, v := range videoMap {
|
||||
if v.LocationID == nil {
|
||||
continue
|
||||
}
|
||||
lidList = append(lidList, *v.LocationID)
|
||||
}
|
||||
var (
|
||||
wg = sync.WaitGroup{}
|
||||
publMap = make(map[uint64]*UserRes)
|
||||
locMap = make(map[ObjectID]*LocInfo)
|
||||
statusMap = make(map[ObjectID]VidStatus)
|
||||
)
|
||||
//一共3个map,每个携程填充一个Map
|
||||
wg.Add(3)
|
||||
common.Go(func() {
|
||||
//PublisherID->User map
|
||||
defer wg.Done()
|
||||
m, err := UserResMap(pidList, uid)
|
||||
if err != nil {
|
||||
log.Error("GetVideoResList PublisherID->User error", log.E(err))
|
||||
return
|
||||
}
|
||||
publMap = m
|
||||
})
|
||||
//Location map
|
||||
common.Go(func() {
|
||||
//LocationID->Location map
|
||||
defer wg.Done()
|
||||
m, err := LocMap(lidList)
|
||||
if err != nil {
|
||||
log.Error("GetVideoResList Location map error", log.E(err))
|
||||
return
|
||||
}
|
||||
locMap = m
|
||||
})
|
||||
//Video Status map
|
||||
common.Go(func() {
|
||||
//Vid->视屏状态 map
|
||||
defer wg.Done()
|
||||
statusMap = VidStatusMap(vidList, uid)
|
||||
})
|
||||
wg.Wait()
|
||||
//组装 []VidInfo
|
||||
videoResList := make([]VideoRes, 0, videoCount)
|
||||
for _, vid := range vidList {
|
||||
info := GetVideoRes(vid, videoMap, publMap, locMap, statusMap)
|
||||
if info.Publisher.UID == nil {
|
||||
log.Warn("Publisher UID is invalid")
|
||||
continue
|
||||
}
|
||||
if info.VideoInfo.ID != nil {
|
||||
commentCount := commentCountMap[*info.VideoInfo.ID]
|
||||
info.CommentCount = &commentCount
|
||||
}
|
||||
info.Publisher.HasFollowed = &videoMap[vid].UInfo.HasFollowed
|
||||
isFree := true
|
||||
info.FreeArea = &isFree
|
||||
videoResList = append(videoResList, info)
|
||||
}
|
||||
return videoResList, nil
|
||||
}
|
||||
|
||||
// RichVidRes RichVidRes
|
||||
type RichVidRes struct {
|
||||
VideoInfo `bson:",inline"` //视屏
|
||||
LocInfo `json:"location" bson:"location"` //位置
|
||||
VidStatus `json:"vidStatus" bson:"vidStatus"` //用户与视频状态信息
|
||||
Publisher `json:"publisher,omitempty" bson:"publisher"` //发布者
|
||||
}
|
||||
|
||||
// GetRichVideoRes GetRichVideoRes
|
||||
func GetRichVideoRes(vid ObjectID, vInfoMap map[ObjectID]*VideoInfo, publMap map[uint64]*UserRes, locMap map[ObjectID]*LocInfo, statusMap map[ObjectID]VidStatus) RichVidRes {
|
||||
res := GetVideoRes(vid, vInfoMap, publMap, locMap, statusMap)
|
||||
if res.VideoInfo.ID == nil {
|
||||
return RichVidRes{}
|
||||
}
|
||||
richRes := RichVidRes(res)
|
||||
return richRes
|
||||
}
|
||||
|
||||
// GetRichVideoResList GetRichVideoResList
|
||||
func GetRichVideoResList(uid uint64, vidList []ObjectID) ([]RichVidRes, error) {
|
||||
return getRichVideoResList(uid, vidList, false)
|
||||
}
|
||||
|
||||
// GetRichVideoResListForSearch 保留当前仅能通过搜索入口访问的视频。
|
||||
func GetRichVideoResListForSearch(uid uint64, vidList []ObjectID) ([]RichVidRes, error) {
|
||||
return getRichVideoResList(uid, vidList, true)
|
||||
}
|
||||
|
||||
func getRichVideoResList(uid uint64, vidList []ObjectID, allowSearchOnly bool) ([]RichVidRes, error) {
|
||||
videoCount := len(vidList)
|
||||
//vid->video Map
|
||||
videoMap, err := videoMap(vidList, uid, allowSearchOnly)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//Publisher
|
||||
//发布者ID List
|
||||
pidList := make([]uint64, 0, videoCount)
|
||||
for _, video := range videoMap {
|
||||
if video.PublisherID == nil {
|
||||
continue
|
||||
}
|
||||
pidList = append(pidList, *video.PublisherID)
|
||||
}
|
||||
//LocationID List
|
||||
lidList := make([]ObjectID, 0, videoCount)
|
||||
for _, v := range videoMap {
|
||||
if v.LocationID == nil {
|
||||
continue
|
||||
}
|
||||
lidList = append(lidList, *v.LocationID)
|
||||
}
|
||||
var (
|
||||
wg = sync.WaitGroup{}
|
||||
publMap = make(map[uint64]*UserRes)
|
||||
locMap = make(map[ObjectID]*LocInfo)
|
||||
statusMap = make(map[ObjectID]VidStatus)
|
||||
)
|
||||
//一共4个map,每个携程填充一个Map
|
||||
wg.Add(3)
|
||||
common.Go(func() {
|
||||
//PublisherID->User map
|
||||
defer wg.Done()
|
||||
m, err := UserResMap(pidList, uid)
|
||||
if err != nil {
|
||||
log.Error("GetVideoResList PublisherID->User error", log.E(err))
|
||||
return
|
||||
}
|
||||
publMap = m
|
||||
})
|
||||
//Location map
|
||||
common.Go(func() {
|
||||
//LocationID->Location map
|
||||
defer wg.Done()
|
||||
m, err := LocMap(lidList)
|
||||
if err != nil {
|
||||
log.Error("GetVideoResList Location map error", log.E(err))
|
||||
return
|
||||
}
|
||||
locMap = m
|
||||
})
|
||||
//Video Status map
|
||||
common.Go(func() {
|
||||
//Vid->视屏状态 map
|
||||
defer wg.Done()
|
||||
statusMap = VidStatusMap(vidList, uid)
|
||||
})
|
||||
wg.Wait()
|
||||
//组装 []RichVidRes
|
||||
videoResList := make([]RichVidRes, 0, videoCount)
|
||||
for _, vid := range vidList {
|
||||
info := GetRichVideoRes(vid, videoMap, publMap, locMap, statusMap)
|
||||
if info.Publisher.UID == nil {
|
||||
continue
|
||||
}
|
||||
videoResList = append(videoResList, info)
|
||||
}
|
||||
return videoResList, nil
|
||||
}
|
||||
|
||||
// VideoListByKeyword 通过关键字匹配title和tagName获取VID List
|
||||
func VideoListByKeyword(keyword string, skip int64, limit int64) ([]vidmod.ObjectID, error) {
|
||||
tags, err := tagmod.GetTagsByKeyword(keyword, 0, 3)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tidList := make([]primitive.ObjectID, len(tags))
|
||||
for i, tag := range tags {
|
||||
tidList[i] = tag.ID
|
||||
}
|
||||
return vidmod.VIDListByKeywordOrTags(keyword, tidList, skip, limit)
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package vidhkwsearcher
|
||||
|
||||
import (
|
||||
"91porn-server/app/service/searcher"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type M = bson.M
|
||||
|
||||
type VideoRes = searcher.VideoRes
|
||||
|
||||
type ObjectID = primitive.ObjectID
|
||||
|
||||
type Video = searcher.VideoInfo
|
||||
|
||||
type Result struct {
|
||||
dat []VideoRes
|
||||
hasNext bool
|
||||
}
|
||||
|
||||
func (*Result) Types() searcher.ResultTypes {
|
||||
return searcher.VidHotKeywordSearchResult
|
||||
}
|
||||
|
||||
func (r *Result) Data() interface{} {
|
||||
return r.dat
|
||||
}
|
||||
|
||||
func (r *Result) Count() int64 {
|
||||
return int64(len(r.dat))
|
||||
}
|
||||
|
||||
func (r *Result) HasNext() bool {
|
||||
return r.hasNext
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package vidhkwsearcher
|
||||
|
||||
import (
|
||||
"91porn-server/common/constant"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/app/service/searcher/vidsearcher"
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/s/kwrankmod"
|
||||
)
|
||||
|
||||
// VidHKWSearcher VidHKWSearcher
|
||||
type VidHKWSearcher struct {
|
||||
uid uint64
|
||||
vidOpt searcher.Opter
|
||||
}
|
||||
|
||||
// NewVidHKWSearcher NewVidHKWSearcher
|
||||
func NewVidHKWSearcher(uid uint64, vidOpt searcher.Opter) searcher.Searcher {
|
||||
return &VidHKWSearcher{
|
||||
uid: uid,
|
||||
vidOpt: vidOpt,
|
||||
}
|
||||
}
|
||||
|
||||
type pair struct {
|
||||
Word string
|
||||
Result searcher.Resulter
|
||||
}
|
||||
|
||||
const timeout = 3 * time.Second
|
||||
const gorouCountMax = 20
|
||||
|
||||
// Search Search
|
||||
func (v *VidHKWSearcher) Search(unuse searcher.KeyWorder, opt searcher.Opter) (result searcher.Resulter, err error) {
|
||||
limitEx1 := opt.Limit() + 1
|
||||
//获取热词
|
||||
words, err := kwrankmod.RecentRanking(limitEx1)
|
||||
if err != nil {
|
||||
return nil, errors.New("vidhkwsearcher RecentRanking error")
|
||||
}
|
||||
//没有热词
|
||||
if len(words) == 0 {
|
||||
return &Result{[]VideoRes{}, false}, nil
|
||||
}
|
||||
hasNext := false
|
||||
if len(words) > int(opt.Limit()) {
|
||||
words = words[:opt.Limit()]
|
||||
hasNext = true
|
||||
}
|
||||
gorouCount := opt.Limit()
|
||||
if gorouCount > gorouCountMax {
|
||||
gorouCount = gorouCountMax
|
||||
}
|
||||
ch := make(chan pair, gorouCount)
|
||||
err_ch := make(chan error)
|
||||
for _, w := range words {
|
||||
word := w.KeyWord
|
||||
common.Go(func() {
|
||||
k := &searcher.Keyword{Word: word}
|
||||
result, err := vidsearcher.NewVidSearcher(v.uid, constant.SearchSP, 0).Search(k, v.vidOpt)
|
||||
if err != nil {
|
||||
err_ch <- err
|
||||
return
|
||||
}
|
||||
pair := pair{
|
||||
Word: word,
|
||||
Result: result,
|
||||
}
|
||||
ch <- pair
|
||||
})
|
||||
}
|
||||
count := len(words)
|
||||
resultMap := make(map[string]searcher.Resulter, count)
|
||||
timer := time.NewTimer(timeout)
|
||||
for i := 0; i < count; i++ {
|
||||
timer.Reset(timeout)
|
||||
select {
|
||||
case pair := <-ch:
|
||||
resultMap[pair.Word] = pair.Result
|
||||
if i == count-1 {
|
||||
return toResult(words, v.vidOpt.Limit(), resultMap, hasNext), nil
|
||||
}
|
||||
case err := <-err_ch: //获取出错前搜索到的视屏
|
||||
log.Error(fmt.Sprintf("VidKWSearcher error:%+v\n", err))
|
||||
return toResult(words, v.vidOpt.Limit(), resultMap, hasNext), nil
|
||||
case <-timer.C: //获取超时前搜索到的视屏
|
||||
log.Warn(fmt.Sprintf("VidKWSearcher timeout::%+v\n", timeout))
|
||||
return toResult(words, v.vidOpt.Limit(), resultMap, hasNext), nil
|
||||
}
|
||||
}
|
||||
return &Result{[]VideoRes{}, false}, nil
|
||||
}
|
||||
|
||||
func toResult(words []kwrankmod.Keyword, vidCountPer int64, resultMap map[string]searcher.Resulter, hasNext bool) searcher.Resulter {
|
||||
resList := make([]VideoRes, 0, vidCountPer)
|
||||
//还原视屏的排序
|
||||
for _, w := range words {
|
||||
word := w.KeyWord
|
||||
result, ok := resultMap[word]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
videoList := result.Data().([]VideoRes)
|
||||
resList = append(resList, videoList...)
|
||||
}
|
||||
return &Result{resList, hasNext}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package vidhotsearcher
|
||||
|
||||
import "91porn-server/app/service/searcher"
|
||||
|
||||
type VideoRes = searcher.VideoRes
|
||||
|
||||
type RichVidRes = searcher.RichVidRes
|
||||
|
||||
type ResultType = int
|
||||
|
||||
const (
|
||||
Video ResultType = iota
|
||||
RichVido
|
||||
)
|
||||
|
||||
type Result struct {
|
||||
dat []VideoRes
|
||||
richDat []RichVidRes
|
||||
typ ResultType
|
||||
hasNext bool
|
||||
}
|
||||
|
||||
func (*Result) Types() searcher.ResultTypes {
|
||||
return searcher.VidPCountSearchResult
|
||||
}
|
||||
|
||||
func (r *Result) Data() interface{} {
|
||||
if r.typ == Video {
|
||||
return r.dat
|
||||
}
|
||||
return r.richDat
|
||||
}
|
||||
|
||||
func (r *Result) Count() int64 {
|
||||
return int64(len(r.dat))
|
||||
}
|
||||
|
||||
func (r *Result) HasNext() bool {
|
||||
return r.hasNext
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package vidhotsearcher
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/s/statvidmod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type ObjectID = primitive.ObjectID
|
||||
|
||||
// VidHotSearcher VidHotSearcher
|
||||
type VidHotSearcher struct {
|
||||
uid uint64
|
||||
typ ResultType
|
||||
}
|
||||
|
||||
// NewVidHotSearcher NewVidHotSearcher
|
||||
func NewVidHotSearcher(uid uint64) searcher.Searcher {
|
||||
return &VidHotSearcher{
|
||||
uid: uid,
|
||||
typ: Video,
|
||||
}
|
||||
}
|
||||
|
||||
// NewRichVidHotSearcher NewRichVidHotSearcher
|
||||
func NewRichVidHotSearcher(uid uint64) searcher.Searcher {
|
||||
return &VidHotSearcher{
|
||||
uid: uid,
|
||||
typ: RichVido,
|
||||
}
|
||||
}
|
||||
|
||||
// Search Search
|
||||
func (v *VidHotSearcher) Search(unuse searcher.KeyWorder, opt searcher.Opter) (result searcher.Resulter, err error) {
|
||||
var (
|
||||
vidList []ObjectID
|
||||
countMap map[ObjectID]int64
|
||||
)
|
||||
limitEx := opt.Limit() + 1
|
||||
if isEarlyMorning1AM(time.Now()) {
|
||||
vidList, err = vidmod.VIDListByCreatedAt(opt.Skip(), limitEx)
|
||||
} else {
|
||||
vidList, countMap, err = statvidmod.GetDailyPlayCountGroup(opt.Skip(), limitEx)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(vidList) == 0 {
|
||||
return &Result{
|
||||
dat: []VideoRes{},
|
||||
richDat: []RichVidRes{},
|
||||
typ: v.typ,
|
||||
hasNext: false,
|
||||
}, nil
|
||||
}
|
||||
hasNext := false
|
||||
if len(vidList) > int(opt.Limit()) {
|
||||
vidList = vidList[:opt.Limit()]
|
||||
hasNext = true
|
||||
}
|
||||
if v.typ == Video {
|
||||
videoResList, err := searcher.GetVideoResList(v.uid, vidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setVideoResListPayCount(videoResList, countMap)
|
||||
return &Result{
|
||||
dat: videoResList,
|
||||
typ: v.typ,
|
||||
hasNext: hasNext,
|
||||
}, nil
|
||||
} else {
|
||||
richVideoResList, err := searcher.GetRichVideoResList(v.uid, vidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setRichVideoResListPayCount(richVideoResList, countMap)
|
||||
return &Result{
|
||||
richDat: richVideoResList,
|
||||
typ: v.typ,
|
||||
hasNext: hasNext,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func setVideoResListPayCount(list []VideoRes, countMap map[ObjectID]int64) {
|
||||
for i := 0; i < len(list); i++ {
|
||||
video := list[i]
|
||||
vid := *video.VideoInfo.ID
|
||||
playCount := int(countMap[vid])
|
||||
video.PlayCount = &playCount
|
||||
list[i] = video
|
||||
}
|
||||
}
|
||||
|
||||
func setRichVideoResListPayCount(list []RichVidRes, countMap map[ObjectID]int64) {
|
||||
for i := 0; i < len(list); i++ {
|
||||
video := list[i]
|
||||
vid := *video.VideoInfo.ID
|
||||
playCount := int(countMap[vid])
|
||||
video.PlayCount = &playCount
|
||||
list[i] = video
|
||||
}
|
||||
}
|
||||
|
||||
//1:00 - 1:10
|
||||
func isEarlyMorning1AM(cur time.Time) bool {
|
||||
start := time.Date(cur.Year(), cur.Month(), cur.Day(), 1, 0, 0, 0, cur.Location())
|
||||
end := time.Date(cur.Year(), cur.Month(), cur.Day(), 1, 10, 0, 0, cur.Location())
|
||||
log.Info("vidhotsearcher isEarlyMorning1AM", log.Any("start", start.String()), log.Any("end", end.String()), log.Any("cur", cur.String()))
|
||||
return cur.After(start) && cur.Before(end)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package vidpcountsearcher
|
||||
|
||||
import "91porn-server/app/service/searcher"
|
||||
|
||||
type VideoRes = searcher.VideoRes
|
||||
|
||||
type RichVidRes = searcher.RichVidRes
|
||||
|
||||
type ResultType = int
|
||||
|
||||
const (
|
||||
Video ResultType = iota
|
||||
RichVido
|
||||
)
|
||||
|
||||
type Result struct {
|
||||
dat []VideoRes
|
||||
richDat []RichVidRes
|
||||
typ ResultType
|
||||
hasNext bool
|
||||
}
|
||||
|
||||
func (*Result) Types() searcher.ResultTypes {
|
||||
return searcher.VidPCountSearchResult
|
||||
}
|
||||
|
||||
func (r *Result) Data() interface{} {
|
||||
if r.typ == Video {
|
||||
return r.dat
|
||||
}
|
||||
return r.richDat
|
||||
}
|
||||
|
||||
func (r *Result) Count() int64 {
|
||||
return int64(len(r.dat))
|
||||
}
|
||||
|
||||
func (r *Result) HasNext() bool {
|
||||
return r.hasNext
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package vidpcountsearcher
|
||||
|
||||
import (
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/models/v/vidmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type ObjectID = primitive.ObjectID
|
||||
|
||||
// VidPlayCountSearcher VidPlayCountSearcher
|
||||
type VidPlayCountSearcher struct {
|
||||
uid uint64
|
||||
typ ResultType
|
||||
}
|
||||
|
||||
// NewVidPlayCountSearcher NewVidPlayCountSearcher
|
||||
func NewVidPlayCountSearcher(uid uint64) searcher.Searcher {
|
||||
return &VidPlayCountSearcher{
|
||||
uid: uid,
|
||||
typ: Video,
|
||||
}
|
||||
}
|
||||
|
||||
// NewRichVidPlayCountSearcher NewRichVidPlayCountSearcher
|
||||
func NewRichVidPlayCountSearcher(uid uint64) searcher.Searcher {
|
||||
return &VidPlayCountSearcher{
|
||||
uid: uid,
|
||||
typ: RichVido,
|
||||
}
|
||||
}
|
||||
|
||||
// Search Search
|
||||
func (v *VidPlayCountSearcher) Search(unuse searcher.KeyWorder, opt searcher.Opter) (result searcher.Resulter, err error) {
|
||||
limitEx := opt.Limit() + 1
|
||||
vidList, err := vidmod.VIDListByFakePlayCount(opt.Skip(), limitEx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(vidList) == 0 {
|
||||
return &Result{
|
||||
typ: v.typ,
|
||||
dat: []VideoRes{},
|
||||
richDat: []RichVidRes{},
|
||||
}, nil
|
||||
}
|
||||
hasNext := false
|
||||
if len(vidList) > int(opt.Limit()) {
|
||||
hasNext = true
|
||||
vidList = vidList[:opt.Limit()]
|
||||
}
|
||||
if v.typ == Video {
|
||||
videoResList, err := searcher.GetVideoResList(v.uid, vidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Result{
|
||||
dat: videoResList,
|
||||
typ: v.typ,
|
||||
hasNext: hasNext,
|
||||
}, nil
|
||||
}
|
||||
richVideoResList, err := searcher.GetRichVideoResList(v.uid, vidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Result{
|
||||
richDat: richVideoResList,
|
||||
typ: v.typ,
|
||||
hasNext: hasNext,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package vidrichsearcher
|
||||
|
||||
import "91porn-server/app/service/searcher"
|
||||
|
||||
type RichVidRes = searcher.RichVidRes
|
||||
|
||||
type VideoSy = searcher.VideoInfo
|
||||
|
||||
type Result struct {
|
||||
dat []RichVidRes
|
||||
hasNext bool
|
||||
}
|
||||
|
||||
func (*Result) Types() searcher.ResultTypes {
|
||||
return searcher.VidRichSearchResult
|
||||
}
|
||||
|
||||
func (r *Result) Data() interface{} {
|
||||
return r.dat
|
||||
}
|
||||
|
||||
func (r *Result) Count() int64 {
|
||||
return int64(len(r.dat))
|
||||
}
|
||||
|
||||
func (r *Result) HasNext() bool {
|
||||
return r.hasNext
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package vidrichsearcher
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"91porn-server/app/appg"
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/app/service/searcher/vidhotsearcher"
|
||||
"91porn-server/models/v/vidmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type ObjectID = primitive.ObjectID
|
||||
|
||||
// VidRichSearcher VidRichSearcher
|
||||
type VidRichSearcher struct {
|
||||
uid uint64
|
||||
}
|
||||
|
||||
// NewVidRichSearcher NewVidRichSearcher
|
||||
func NewVidRichSearcher(uid uint64) searcher.Searcher {
|
||||
return &VidRichSearcher{
|
||||
uid: uid,
|
||||
}
|
||||
}
|
||||
|
||||
// Search Search
|
||||
func (v *VidRichSearcher) Search(keyword searcher.KeyWorder, opt searcher.Opter) (result searcher.Resulter, err error) {
|
||||
kw, ok := keyword.Get().(string)
|
||||
if !ok {
|
||||
return nil, errors.New("keyword type error")
|
||||
}
|
||||
if kw == "" { //关键字为空,热搜
|
||||
return vidhotsearcher.NewRichVidHotSearcher(v.uid).Search(nil, opt)
|
||||
}
|
||||
limitEx1 := opt.Limit() + 1
|
||||
var vidList []vidmod.ObjectID
|
||||
if appg.Conf.Elastic.IsActive {
|
||||
vidList, err = esSearch(kw, opt)
|
||||
} else {
|
||||
vidList, err = searcher.VideoListByKeyword(kw, opt.Skip(), limitEx1)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(vidList) == 0 {
|
||||
return &Result{[]RichVidRes{}, false}, nil
|
||||
}
|
||||
hasNext := false
|
||||
if len(vidList) > int(opt.Limit()) {
|
||||
hasNext = true
|
||||
vidList = vidList[:opt.Limit()]
|
||||
}
|
||||
richVideoResList, err := searcher.GetRichVideoResListForSearch(v.uid, vidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Result{richVideoResList, hasNext}, nil
|
||||
}
|
||||
|
||||
func esSearch(keyword string, opt searcher.Opter) ([]vidmod.ObjectID, error) {
|
||||
esVideos, err := vidmod.Search(keyword, opt.Skip(), opt.Limit())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
esVideosLen := len(esVideos)
|
||||
if esVideosLen == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
videosID := make([]vidmod.ObjectID, esVideosLen)
|
||||
for i, v := range esVideos {
|
||||
videosID[i] = v.ID
|
||||
}
|
||||
return videosID, nil
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package vidsearcher
|
||||
|
||||
import (
|
||||
"91porn-server/app/service/searcher"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// M M
|
||||
type M = bson.M
|
||||
|
||||
// A A
|
||||
type A = bson.A
|
||||
|
||||
type VideoRes = searcher.VideoRes
|
||||
|
||||
type ObjectID = primitive.ObjectID
|
||||
|
||||
type Video = searcher.VideoInfo
|
||||
|
||||
type Result struct {
|
||||
dat []VideoRes
|
||||
hasNext bool
|
||||
}
|
||||
|
||||
func (*Result) Types() searcher.ResultTypes {
|
||||
return searcher.VidSearchResult
|
||||
}
|
||||
|
||||
func (r *Result) Data() interface{} {
|
||||
return r.dat
|
||||
}
|
||||
|
||||
func (r *Result) Count() int64 {
|
||||
return int64(len(r.dat))
|
||||
}
|
||||
|
||||
func (r *Result) HasNext() bool {
|
||||
return r.hasNext
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package vidsearcher
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"91porn-server/app/appg"
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/app/service/searcher/vidhotsearcher"
|
||||
"91porn-server/models/v/vidmod"
|
||||
)
|
||||
|
||||
// VidSearcher VidSearcher
|
||||
type VidSearcher struct {
|
||||
uid uint64
|
||||
vidType string
|
||||
sortType int
|
||||
}
|
||||
|
||||
// NewVidSearcher NewVidSearcher
|
||||
func NewVidSearcher(uid uint64, vidType string, sortType int) searcher.Searcher {
|
||||
return &VidSearcher{uid: uid, vidType: vidType, sortType: sortType}
|
||||
}
|
||||
|
||||
// Search Search
|
||||
func (v *VidSearcher) Search(keyword searcher.KeyWorder, opt searcher.Opter) (result searcher.Resulter, err error) {
|
||||
kw, ok := keyword.Get().(string)
|
||||
if !ok {
|
||||
return nil, errors.New("keyword type error")
|
||||
}
|
||||
if kw == "" { //关键字为空,热搜
|
||||
return vidhotsearcher.NewVidHotSearcher(v.uid).Search(nil, opt)
|
||||
}
|
||||
limitEx1 := opt.Limit() + 1
|
||||
var vidList []vidmod.ObjectID
|
||||
if appg.Conf.Elastic.IsActive { // 使用ES搜索视频
|
||||
vidList, err = esSearch(kw, v.vidType, opt, v.sortType)
|
||||
} else {
|
||||
vidList, err = searcher.VideoListByKeyword(kw, opt.Skip(), limitEx1)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(vidList) == 0 {
|
||||
return &Result{[]VideoRes{}, false}, nil
|
||||
}
|
||||
hasNext := false
|
||||
if len(vidList) > int(opt.Limit()) {
|
||||
hasNext = true
|
||||
vidList = vidList[:opt.Limit()]
|
||||
}
|
||||
videoResList, err := searcher.GetVideoResListForSearch(v.uid, vidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Result{videoResList, hasNext}, nil
|
||||
}
|
||||
|
||||
func esSearch(keyword string, vidType string, opt searcher.Opter, sortType int) ([]vidmod.ObjectID, error) {
|
||||
esVideos, err := vidmod.DirectSearch(keyword, vidType, sortType, opt.Skip(), opt.Limit()+1)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
esVideosLen := len(esVideos)
|
||||
if esVideosLen == 0 {
|
||||
return nil, err
|
||||
}
|
||||
videosID := make([]vidmod.ObjectID, esVideosLen)
|
||||
for i, v := range esVideos {
|
||||
videosID[i] = v.ID
|
||||
}
|
||||
return videosID, nil
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package vidtagsearcher
|
||||
|
||||
import (
|
||||
"91porn-server/app/service/searcher"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type VideoRes = searcher.VideoRes
|
||||
|
||||
type ObjectID = primitive.ObjectID
|
||||
|
||||
type Video = searcher.VideoInfo
|
||||
|
||||
type Result struct {
|
||||
dat []VideoRes
|
||||
hasNext bool
|
||||
}
|
||||
|
||||
func (*Result) Types() searcher.ResultTypes {
|
||||
return searcher.VidTagSearchResult
|
||||
}
|
||||
|
||||
func (r *Result) Data() interface{} {
|
||||
return r.dat
|
||||
}
|
||||
|
||||
func (r *Result) Count() int64 {
|
||||
return int64(len(r.dat))
|
||||
}
|
||||
|
||||
func (r *Result) HasNext() bool {
|
||||
return r.hasNext
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package vidtagsearcher
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/models/v/vidmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
// M M
|
||||
type M = bson.M
|
||||
|
||||
// A A
|
||||
type A = bson.A
|
||||
|
||||
// VidTagSearcher VidTagSearcher
|
||||
type VidTagSearcher struct {
|
||||
uid uint64
|
||||
}
|
||||
|
||||
var (
|
||||
videoListByTagID = vidmod.VideoListByTagID
|
||||
getVideoResListForSearch = searcher.GetVideoResListForSearch
|
||||
)
|
||||
|
||||
// NewVidWonderSearcher NewVidWonderSearcher
|
||||
func NewVidTagSearcher(uid uint64) searcher.Searcher {
|
||||
return &VidTagSearcher{
|
||||
uid: uid,
|
||||
}
|
||||
}
|
||||
|
||||
// Search Search
|
||||
func (v *VidTagSearcher) Search(tid searcher.KeyWorder, opt searcher.Opter) (result searcher.Resulter, err error) {
|
||||
tagId, ok := tid.Get().(ObjectID)
|
||||
if !ok {
|
||||
return nil, errors.New("keyword type error")
|
||||
}
|
||||
limitEx1 := opt.Limit() + 1
|
||||
videoList, err := videoListByTagID(tagId, opt.Sort(), opt.Skip(), limitEx1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(videoList) == 0 {
|
||||
return &Result{[]VideoRes{}, false}, nil
|
||||
}
|
||||
hasNext := false
|
||||
if len(videoList) > int(opt.Limit()) {
|
||||
hasNext = true
|
||||
videoList = videoList[:opt.Limit()]
|
||||
}
|
||||
vidList := make([]ObjectID, len(videoList))
|
||||
for i, video := range videoList {
|
||||
vidList[i] = video.ID
|
||||
}
|
||||
videoResList, err := getVideoResListForSearch(v.uid, vidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Result{videoResList, hasNext}, nil
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package vidtagsearcher
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/models/v/vidmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
func TestSearchUsesSearchVisibleVideoResults(t *testing.T) {
|
||||
originalVideoListByTagID := videoListByTagID
|
||||
originalGetVideoResListForSearch := getVideoResListForSearch
|
||||
t.Cleanup(func() {
|
||||
videoListByTagID = originalVideoListByTagID
|
||||
getVideoResListForSearch = originalGetVideoResListForSearch
|
||||
})
|
||||
|
||||
tagID := primitive.NewObjectID()
|
||||
videoIDs := []primitive.ObjectID{
|
||||
primitive.NewObjectID(),
|
||||
primitive.NewObjectID(),
|
||||
primitive.NewObjectID(),
|
||||
}
|
||||
videoListByTagID = func(gotTagID primitive.ObjectID, sort bson.D, skip, limit int64) ([]vidmod.VideoModel, error) {
|
||||
if gotTagID != tagID {
|
||||
t.Fatalf("tagID = %s, want %s", gotTagID.Hex(), tagID.Hex())
|
||||
}
|
||||
if skip != 0 || limit != 3 {
|
||||
t.Fatalf("skip/limit = %d/%d, want 0/3", skip, limit)
|
||||
}
|
||||
return []vidmod.VideoModel{
|
||||
{ID: videoIDs[0]},
|
||||
{ID: videoIDs[1]},
|
||||
{ID: videoIDs[2]},
|
||||
}, nil
|
||||
}
|
||||
|
||||
const uid = uint64(17803584)
|
||||
searchAccessToken := "search-token"
|
||||
getVideoResListForSearch = func(gotUID uint64, gotVideoIDs []primitive.ObjectID) ([]searcher.VideoRes, error) {
|
||||
if gotUID != uid {
|
||||
t.Fatalf("uid = %d, want %d", gotUID, uid)
|
||||
}
|
||||
if !reflect.DeepEqual(gotVideoIDs, videoIDs[:2]) {
|
||||
t.Fatalf("video IDs = %v, want %v", gotVideoIDs, videoIDs[:2])
|
||||
}
|
||||
return []searcher.VideoRes{
|
||||
{VideoInfo: searcher.VideoInfo{ID: &gotVideoIDs[0], SearchAccessToken: &searchAccessToken}},
|
||||
{VideoInfo: searcher.VideoInfo{ID: &gotVideoIDs[1], SearchAccessToken: &searchAccessToken}},
|
||||
}, nil
|
||||
}
|
||||
|
||||
option := &searcher.Option{}
|
||||
option.SetLimit(2)
|
||||
result, err := NewVidTagSearcher(uid).Search(&searcher.ObjID{ID: tagID}, option)
|
||||
if err != nil {
|
||||
t.Fatalf("Search() error = %v", err)
|
||||
}
|
||||
if !result.HasNext() {
|
||||
t.Fatal("Search() hasNext = false, want true")
|
||||
}
|
||||
videos, ok := result.Data().([]searcher.VideoRes)
|
||||
if !ok {
|
||||
t.Fatalf("Search() data type = %T, want []searcher.VideoRes", result.Data())
|
||||
}
|
||||
if len(videos) != 2 {
|
||||
t.Fatalf("Search() result length = %d, want 2", len(videos))
|
||||
}
|
||||
for i, video := range videos {
|
||||
if video.SearchAccessToken == nil || *video.SearchAccessToken != searchAccessToken {
|
||||
t.Fatalf("video %d searchAccessToken = %v, want %q", i, video.SearchAccessToken, searchAccessToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package vidtonesearcher
|
||||
|
||||
import (
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/v/tonerecomod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
)
|
||||
|
||||
type freeAreaSearcher struct {
|
||||
uid uint64
|
||||
}
|
||||
|
||||
func newFreeAreaSearcher(uid uint64) searcher.Searcher {
|
||||
return &freeAreaSearcher{
|
||||
uid: uid,
|
||||
}
|
||||
}
|
||||
|
||||
// Search Search
|
||||
func (f *freeAreaSearcher) Search(unuse searcher.KeyWorder, opt searcher.Opter) (result searcher.Resulter, err error) {
|
||||
limitEx1 := opt.Limit() + 1
|
||||
vidList, err := vidmod.GetFreeVidList(opt.Sort(), opt.Skip(), limitEx1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(vidList) == 0 {
|
||||
return &Result{
|
||||
&Resp{
|
||||
Theme: tonerecomod.FreeArea,
|
||||
List: []VideoRes{},
|
||||
PayCount: &zero,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
hasNext := false
|
||||
if len(vidList) > int(opt.Limit()) {
|
||||
hasNext = true
|
||||
vidList = vidList[:opt.Limit()]
|
||||
}
|
||||
videoInfoList, err := searcher.GetVideoResList(f.uid, vidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Info("freeArea search result", log.Any("totalVideoCount", len(vidList)), log.Any("respVideoCount", len(videoInfoList)))
|
||||
freeAreaPlayCount := GetToneValue().FreeAreaPlayCount
|
||||
return &Result{
|
||||
&Resp{
|
||||
List: videoInfoList,
|
||||
Theme: tonerecomod.FreeArea,
|
||||
PlayCount: &freeAreaPlayCount,
|
||||
HasNext: hasNext,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package vidtonesearcher
|
||||
|
||||
import (
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/s/statvidtotalmod"
|
||||
"91porn-server/models/v/tonerecomod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
type ObjectID = primitive.ObjectID
|
||||
|
||||
type VideoSy = searcher.VideoInfo
|
||||
|
||||
// VidToneSearcher VidToneSearcher
|
||||
type goldCoinAreaSearcher struct {
|
||||
uid uint64
|
||||
}
|
||||
|
||||
var zero = int64(0)
|
||||
|
||||
// newGoldCoinAreaSearcher newGoldCoinAreaSearcher
|
||||
func newGoldCoinAreaSearcher(uid uint64) searcher.Searcher {
|
||||
return &goldCoinAreaSearcher{
|
||||
uid: uid,
|
||||
}
|
||||
}
|
||||
|
||||
// Search Search
|
||||
func (g *goldCoinAreaSearcher) Search(unuse searcher.KeyWorder, opt searcher.Opter) (result searcher.Resulter, err error) {
|
||||
limitEx1 := opt.Limit() + 1
|
||||
vidList, err := vidmod.GetPayVidList(opt.Sort(), opt.Skip(), limitEx1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(vidList) == 0 {
|
||||
return &Result{
|
||||
&Resp{
|
||||
Theme: tonerecomod.GoldCoinArea,
|
||||
List: []VideoRes{},
|
||||
PayCount: &zero,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
hasNext := false
|
||||
if len(vidList) > int(opt.Limit()) {
|
||||
hasNext = true
|
||||
vidList = vidList[:opt.Limit()]
|
||||
}
|
||||
videoInfoList, err := searcher.GetVideoResList(g.uid, vidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Info("goldCoinArea search result", log.Any("totalVideoCount", len(vidList)), log.Any("respVideoCount", len(videoInfoList)))
|
||||
payCountMap, err := statvidtotalmod.GetPayCountMap(vidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
videoInfoPCList := make([]VideoRes, 0, len(videoInfoList))
|
||||
for _, info := range videoInfoList {
|
||||
if info.VideoInfo.ID == nil {
|
||||
continue
|
||||
}
|
||||
id := *info.VideoInfo.ID
|
||||
payCount := int(payCountMap[id])
|
||||
info.VideoInfo.PayCount = &payCount
|
||||
videoInfoPCList = append(videoInfoPCList, info)
|
||||
}
|
||||
goldCoinAreaPayCount := GetToneValue().GoldCoinAreaPayCount
|
||||
return &Result{
|
||||
&Resp{
|
||||
List: videoInfoPCList,
|
||||
Theme: tonerecomod.GoldCoinArea,
|
||||
PayCount: &goldCoinAreaPayCount,
|
||||
HasNext: hasNext,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package vidtonesearcher
|
||||
|
||||
import (
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/models/v/tonerecomod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
)
|
||||
|
||||
// latestUpload latestUpload
|
||||
type latestUploadSearcher struct {
|
||||
uid uint64
|
||||
}
|
||||
|
||||
// latestUpload latestUpload
|
||||
func newLatestUploadSearcher(uid uint64) searcher.Searcher {
|
||||
return &latestUploadSearcher{
|
||||
uid: uid,
|
||||
}
|
||||
}
|
||||
|
||||
// Search Search
|
||||
func (l *latestUploadSearcher) Search(unuse searcher.KeyWorder, opt searcher.Opter) (result searcher.Resulter, err error) {
|
||||
limit := opt.Limit()
|
||||
skip := opt.Skip()
|
||||
limitEx1 := limit + 1
|
||||
vidList, err := vidmod.VIDListByCreatedAt(skip, limitEx1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(vidList) == 0 {
|
||||
return &Result{
|
||||
&Resp{
|
||||
Theme: tonerecomod.LatestUpload,
|
||||
List: []VideoRes{},
|
||||
PlayCount: &zero,
|
||||
HasNext: false,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
hasNext := false
|
||||
if len(vidList) > int(limit) {
|
||||
hasNext = true
|
||||
vidList = vidList[:opt.Limit()]
|
||||
}
|
||||
videoResList, err := searcher.GetVideoResList(l.uid, vidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
latestUploadPlayCount := GetToneValue().LatestUploadPlayCount
|
||||
return &Result{
|
||||
&Resp{
|
||||
List: videoResList,
|
||||
Theme: tonerecomod.LatestUpload,
|
||||
PlayCount: &latestUploadPlayCount,
|
||||
HasNext: hasNext,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package vidtonesearcher
|
||||
|
||||
import (
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/models/v/tonerecomod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
)
|
||||
|
||||
// mostCommentsSearcher mostCommentsSearcher
|
||||
type mostCommentsSearcher struct {
|
||||
uid uint64
|
||||
}
|
||||
|
||||
// newMostCommentsSearcher newMostCommentsSearcher
|
||||
func newMostCommentsSearcher(uid uint64) searcher.Searcher {
|
||||
return &mostCommentsSearcher{
|
||||
uid: uid,
|
||||
}
|
||||
}
|
||||
|
||||
// Search Search
|
||||
func (g *mostCommentsSearcher) Search(unuse searcher.KeyWorder, opt searcher.Opter) (result searcher.Resulter, err error) {
|
||||
limit := opt.Limit()
|
||||
limitEx1 := limit + 1
|
||||
vidList, err := vidmod.VIDListSortByFakeCommentCount(opt.Skip(), limitEx1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(vidList) == 0 {
|
||||
return &Result{
|
||||
&Resp{
|
||||
Theme: tonerecomod.MostComments,
|
||||
List: []VideoRes{},
|
||||
PlayCount: &zero,
|
||||
HasNext: false,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
hasNext := false
|
||||
if len(vidList) > int(limit) {
|
||||
hasNext = true
|
||||
vidList = vidList[:opt.Limit()]
|
||||
}
|
||||
videoResList, err := searcher.GetVideoResList(g.uid, vidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mostCommentsPlayCount := GetToneValue().MostCommentsPlayCount
|
||||
return &Result{
|
||||
&Resp{
|
||||
List: videoResList,
|
||||
Theme: tonerecomod.MostComments,
|
||||
PlayCount: &mostCommentsPlayCount,
|
||||
HasNext: hasNext,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package vidtonesearcher
|
||||
|
||||
import (
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/models/v/tonerecomod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
)
|
||||
|
||||
// latestUpload latestUpload
|
||||
type mostLikesSearcher struct {
|
||||
uid uint64
|
||||
}
|
||||
|
||||
// latestUpload latestUpload
|
||||
func newMostLikesSearcher(uid uint64) searcher.Searcher {
|
||||
return &mostLikesSearcher{
|
||||
uid: uid,
|
||||
}
|
||||
}
|
||||
|
||||
// Search Search
|
||||
func (g *mostLikesSearcher) Search(unuse searcher.KeyWorder, opt searcher.Opter) (result searcher.Resulter, err error) {
|
||||
limit := opt.Limit()
|
||||
limitEx1 := limit + 1
|
||||
vidList, err := vidmod.VIDListByFakeLikeCount(opt.Skip(), limitEx1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(vidList) == 0 {
|
||||
return &Result{
|
||||
&Resp{
|
||||
Theme: tonerecomod.MostLikes,
|
||||
List: []VideoRes{},
|
||||
PlayCount: &zero,
|
||||
HasNext: false,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
hasNext := false
|
||||
if len(vidList) > int(limit) {
|
||||
hasNext = true
|
||||
vidList = vidList[:opt.Limit()]
|
||||
}
|
||||
videoResList, err := searcher.GetVideoResList(g.uid, vidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mostLikesPlayCount := GetToneValue().MostLikesPlayCount
|
||||
return &Result{
|
||||
&Resp{
|
||||
List: videoResList,
|
||||
Theme: tonerecomod.MostLikes,
|
||||
PlayCount: &mostLikesPlayCount,
|
||||
HasNext: hasNext,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package vidtonesearcher
|
||||
|
||||
import (
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/models/v/tonerecomod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
)
|
||||
|
||||
// VidToneSearcher VidToneSearcher
|
||||
type officialRecomSearcher struct {
|
||||
uid uint64
|
||||
}
|
||||
|
||||
// officialRecomSearcher officialRecomSearcher
|
||||
func newOfficialRecomSearcher(uid uint64) searcher.Searcher {
|
||||
return &officialRecomSearcher{
|
||||
uid: uid,
|
||||
}
|
||||
}
|
||||
|
||||
// Search Search
|
||||
func (g *officialRecomSearcher) Search(unuse searcher.KeyWorder, opt searcher.Opter) (result searcher.Resulter, err error) {
|
||||
limitEx1 := opt.Limit() + 1
|
||||
vidList, err := vidmod.VIDListToneOfficialRecom(opt.Skip(), limitEx1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(vidList) == 0 {
|
||||
return &Result{
|
||||
&Resp{
|
||||
Theme: tonerecomod.OfficialRecom,
|
||||
List: []VideoRes{},
|
||||
PlayCount: &zero,
|
||||
HasNext: false,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
hasNext := false
|
||||
if len(vidList) > int(opt.Limit()) {
|
||||
hasNext = true
|
||||
vidList = vidList[:opt.Limit()]
|
||||
}
|
||||
videoResList, err := searcher.GetVideoResList(g.uid, vidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
officialRecomPlayCount := GetToneValue().OfficialRecomPlayCount
|
||||
return &Result{
|
||||
&Resp{
|
||||
List: videoResList,
|
||||
Theme: tonerecomod.OfficialRecom,
|
||||
PlayCount: &officialRecomPlayCount,
|
||||
HasNext: hasNext,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package vidtonesearcher
|
||||
|
||||
import (
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/models/v/tonerecomod"
|
||||
"91porn-server/models/v/vidmod"
|
||||
)
|
||||
|
||||
// playAtMostSearcher playAtMostSearcher
|
||||
type playAtMostSearcher struct {
|
||||
uid uint64
|
||||
}
|
||||
|
||||
// newPlayAtMostSearcher newPlayAtMostSearcher
|
||||
func newPlayAtMostSearcher(uid uint64) searcher.Searcher {
|
||||
return &playAtMostSearcher{
|
||||
uid: uid,
|
||||
}
|
||||
}
|
||||
|
||||
// Search Search
|
||||
func (g *playAtMostSearcher) Search(unuse searcher.KeyWorder, opt searcher.Opter) (result searcher.Resulter, err error) {
|
||||
limit := opt.Limit()
|
||||
limitEx1 := limit + 1
|
||||
vidList, err := vidmod.VIDListByFakePlayCount(opt.Skip(), limitEx1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(vidList) == 0 {
|
||||
return &Result{
|
||||
&Resp{
|
||||
Theme: tonerecomod.PlayAtMost,
|
||||
List: []VideoRes{},
|
||||
PlayCount: &zero,
|
||||
HasNext: false,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
hasNext := false
|
||||
if len(vidList) > int(limit) {
|
||||
hasNext = true
|
||||
vidList = vidList[:opt.Limit()]
|
||||
}
|
||||
videoResList, err := searcher.GetVideoResList(g.uid, vidList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
playAtMostPlayCount := GetToneValue().PlayAtMostPlayCount
|
||||
return &Result{
|
||||
&Resp{
|
||||
List: videoResList,
|
||||
Theme: tonerecomod.PlayAtMost,
|
||||
PlayCount: &playAtMostPlayCount,
|
||||
HasNext: hasNext,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package vidtonesearcher
|
||||
|
||||
import (
|
||||
"91porn-server/app/service/searcher"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
// M M
|
||||
type M = bson.M
|
||||
|
||||
// A A
|
||||
type A = bson.A
|
||||
|
||||
type VideoRes = searcher.VideoRes
|
||||
|
||||
type Resp struct {
|
||||
List []VideoRes `json:"list"`
|
||||
Theme string `json:"theme"`
|
||||
PlayCount *int64 `json:"playCount,omitempty"`
|
||||
PayCount *int64 `json:"payCount,omitempty"`
|
||||
HasNext bool `json:"hasNext"`
|
||||
}
|
||||
|
||||
type Result struct {
|
||||
dat *Resp
|
||||
}
|
||||
|
||||
const MaxVideoCount = 400
|
||||
|
||||
func (*Result) Types() searcher.ResultTypes {
|
||||
return searcher.VidToneSearchResult
|
||||
}
|
||||
|
||||
func (r *Result) Data() interface{} {
|
||||
return r.dat
|
||||
}
|
||||
|
||||
func (r *Result) Count() int64 {
|
||||
if r.dat == nil {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func (r *Result) HasNext() bool {
|
||||
if r.dat == nil {
|
||||
return false
|
||||
}
|
||||
return r.dat.HasNext
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package vidtonesearcher
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"91porn-server/app/appg"
|
||||
"91porn-server/app/service/searcher"
|
||||
"91porn-server/common/constant/redisconst"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/models/v/tonemod"
|
||||
"91porn-server/models/v/tonerecomod"
|
||||
)
|
||||
|
||||
type ToneValue = tonemod.ToneValue
|
||||
|
||||
// VidToneSearcher VidToneSearcher
|
||||
type VidToneSearcher struct {
|
||||
uid uint64
|
||||
theme tonerecomod.ThemeType
|
||||
}
|
||||
|
||||
// NewVidToneSearcher NewVidToneSearcher
|
||||
func NewVidToneSearcher(theme tonerecomod.ThemeType, uid uint64) searcher.Searcher {
|
||||
return &VidToneSearcher{
|
||||
uid: uid,
|
||||
theme: theme,
|
||||
}
|
||||
}
|
||||
|
||||
// Realm Realm
|
||||
func (searcher *VidToneSearcher) Theme() string {
|
||||
return searcher.theme
|
||||
}
|
||||
|
||||
// Search Search
|
||||
func (v *VidToneSearcher) Search(keyword searcher.KeyWorder, opt searcher.Opter) (result searcher.Resulter, err error) {
|
||||
switch v.theme {
|
||||
case tonerecomod.OfficialRecom:
|
||||
return newOfficialRecomSearcher(v.uid).Search(nil, opt)
|
||||
case tonerecomod.GoldCoinArea:
|
||||
return newGoldCoinAreaSearcher(v.uid).Search(nil, opt)
|
||||
case tonerecomod.LatestUpload:
|
||||
return newLatestUploadSearcher(v.uid).Search(nil, opt)
|
||||
case tonerecomod.MostLikes:
|
||||
return newMostLikesSearcher(v.uid).Search(nil, opt)
|
||||
case tonerecomod.PlayAtMost:
|
||||
return newPlayAtMostSearcher(v.uid).Search(nil, opt)
|
||||
case tonerecomod.MostComments:
|
||||
return newMostCommentsSearcher(v.uid).Search(nil, opt)
|
||||
case tonerecomod.FreeArea:
|
||||
return newFreeAreaSearcher(v.uid).Search(nil, opt)
|
||||
default:
|
||||
return nil, fmt.Errorf("search vidtonesearcher invalid theme: %s", v.theme)
|
||||
}
|
||||
}
|
||||
|
||||
func GetToneValue() ToneValue {
|
||||
var toneValue ToneValue
|
||||
redisKey := redisconst.ToneKey()
|
||||
if err := appg.Redis.Scan(redisKey, &toneValue); err != nil {
|
||||
log.Warn("search vidtonesearcher GetToneValue Scan error", log.E(err))
|
||||
}
|
||||
return toneValue
|
||||
}
|
||||
Reference in New Issue
Block a user