Files
huangguo_server/app/service/vidser/vidser.go
T
rootandClaude Opus 5 8679200f41 Initial commit
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 13:57:10 +08:00

2886 lines
86 KiB
Go

package vidser
import (
"91porn-server/app/service/mediaser"
"91porn-server/app/service/searchaccessser"
"91porn-server/app/service/shortrecommendser"
"91porn-server/app/service/sys_config"
"91porn-server/common/cachev2"
"91porn-server/common/redis"
topser "91porn-server/common/top"
"91porn-server/common/top/weektop"
"91porn-server/models/cache/viddata"
"91porn-server/models/v/mediamod"
"context"
"encoding/json"
"errors"
"fmt"
"math"
"net/http"
"sort"
"strconv"
"strings"
"sync"
"time"
goredis "github.com/go-redis/redis"
"github.com/vmihailenco/msgpack/v5"
"91porn-server/app/appg"
"91porn-server/app/proto"
"91porn-server/app/service/activityclient"
"91porn-server/app/service/searcher"
"91porn-server/app/service/searcher/vidhkwsearcher"
"91porn-server/app/service/searcher/vidhotsearcher"
"91porn-server/app/service/vidhelpser"
"91porn-server/common"
"91porn-server/common/constant"
"91porn-server/common/constant/redisconst"
"91porn-server/common/httputil"
"91porn-server/common/log"
sli "91porn-server/common/slice"
"91porn-server/common/stderr"
"91porn-server/common/timeutil"
"91porn-server/models/commod"
"91porn-server/models/l/payvidlgmod"
"91porn-server/models/l/playlgmod"
"91porn-server/models/s/hotstatmod"
"91porn-server/models/s/sectionstatmod"
"91porn-server/models/v/freeVidmod"
"91porn-server/models/v/imagetopmod"
"91porn-server/models/v/locmod"
"91porn-server/models/v/moduleconfmod"
"91porn-server/models/v/modulesectionmod"
"91porn-server/models/v/modulevidmod"
"91porn-server/models/v/productmod"
"91porn-server/models/v/tagmod"
"91porn-server/models/v/useractmod"
"91porn-server/models/v/usermod"
"91porn-server/models/v/userwatchrecordmod"
"91porn-server/models/v/videoactivitymod"
"91porn-server/models/v/videodiscountmod"
"91porn-server/models/v/vidmod"
"91porn-server/models/v/vidpopmod"
"91porn-server/models/v/walletmod"
"github.com/jinzhu/now"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
const (
h5FirstPlay = "1"
timeout = 5 * time.Second
)
var defaultLocation, _ = primitive.ObjectIDFromHex("5dbeb23790b3d9aa3756912f") //默认城市ID 广州
func getCityByIP(ip string) string {
return vidhelpser.GetLocationByIP(ip)
}
// GetLocationList 获取附近位置的视频
func GetLocationList(uid uint64, ip string, city string, page, size uint64, typ string) (stderr.Code, interface{}) {
if len(city) == 0 {
city = getCityByIP(ip)
}
lid, err := locmod.GetLocationIDByCity(city)
if err != nil {
return stderr.ErrDbQueryError, nil
}
if lid.IsZero() {
return stderr.Success, vidmod.LocVideoResp{List: []*vidmod.VideoInfo{}, HasNext: false}
}
var videos []*vidmod.VideoModel
if typ == vidmod.SP {
if videos, err = vidmod.GetVideoListByLocation(lid, int64(page), int64(size+1)); err != nil {
return stderr.ErrDbQueryError, nil
}
}
if typ == vidmod.COVER {
if videos, err = vidmod.GetImageListByLocation(lid, int64(page), int64(size+1)); err != nil {
return stderr.ErrDbQueryError, nil
}
}
hasNext := false
if uint64(len(videos)) > size {
videos = videos[:size]
hasNext = true
}
return stderr.Success, vidmod.LocVideoResp{List: vidhelpser.EncodeVideoInfo(uid, videos), HasNext: hasNext}
}
// GetLocationHot 获取热门城市
func GetLocationHot() (code stderr.Code, data interface{}) {
citys, err := locmod.GetHotCity()
if err != nil {
return stderr.ErrDbQueryError, nil
}
back := make([]locmod.CityResp, len(citys))
for i, c := range citys {
back[i] = locmod.CityResp{ID: c.ID.Hex(), City: c.City, Province: c.Province, Hot: c.SortKey}
}
return stderr.Success, back
}
// GetInfo 获取视频详情
func GetInfo(uid uint64, videoID string) (code stderr.Code, data interface{}) {
return GetInfoWithSearchAccess(uid, videoID, "")
}
// GetInfoWithSearchAccess 获取视频详情;失效亚模块的视频必须携带搜索结果签发的访问凭证。
func GetInfoWithSearchAccess(uid uint64, videoID, searchAccessToken string) (code stderr.Code, data interface{}) {
v, err := vidmod.GetVideoInfo(videoID)
if err != nil {
return stderr.ErrDbQueryError, nil
}
blockedModules, err := moduleconfmod.BlockedOutsideSearchModuleIDs([]string{v.MID}, time.Now())
if err != nil {
return stderr.ErrDbQueryError, nil
}
_, searchOnly := blockedModules[v.MID]
if searchOnly && !searchaccessser.Validate(searchAccessToken, uid, videoID, time.Now()) {
return stderr.CodeEmptyData, nil
}
var infos []*vidmod.VideoInfo
if searchOnly {
infos = vidhelpser.EncodeVideoInfoForSearch(uid, []*vidmod.VideoModel{&v})
} else {
infos = vidhelpser.EncodeVideoInfo(uid, []*vidmod.VideoModel{&v})
}
if len(infos) == 0 {
return stderr.UnkonwErr, ""
}
info := infos[0]
if info.NewsType == constant.COVER {
user, _ := usermod.FindUserByUID(uid)
wallet, _ := walletmod.GetWallet(uid)
info.RichText = activityclient.ReplaceActivityDomainInHTML(info.RichText, user, wallet)
}
return stderr.Success, info
}
// SubmitBase 提交基本信息
func SubmitBase(uid uint64, req vidmod.SubmitReq, clientIP string) (code stderr.Code, vid primitive.ObjectID) {
if uid == 0 {
uid = req.UID
}
tags := make([]primitive.ObjectID, 0, len(req.Tags))
for _, t := range req.Tags {
oid, err := primitive.ObjectIDFromHex(t)
if err != nil {
log.Warn("SubmitBase error", log.Any("uid", uid), log.Any("req", req), log.E(err))
continue
}
if sli.PrimitiveObjIDIsExist(tags, oid) {
continue
}
tags = append(tags, oid)
// 标签视频数计数
if err = tagmod.IncreaseTagVidCount(oid, 1); err != nil {
log.Warn("SubmitBase2 error", log.Any("uid", uid), log.Any("req", req), log.E(err))
continue
}
}
l := locmod.Location{
Longitude: req.Location.Longitude,
Latitude: req.Location.Latitude,
Country: req.Location.Country,
CountryCode: req.Location.CountryCode,
Province: req.Location.Province,
ProvinceCode: req.Location.ProvinceCode,
City: req.Location.City,
CityCode: req.Location.CityCode,
Address: req.Location.Address,
Visit: 0,
}
isHideLocation := false
if len(l.City) == 0 {
l.City = getCityByIP(clientIP)
isHideLocation = true
}
var lid primitive.ObjectID
if err := locmod.InsertLocationInfo(l); err != nil {
lid = defaultLocation
} else {
lid, err = locmod.GetLocationIDByCity(l.City)
if err != nil {
user, userError := usermod.FindUserByUID(uid)
if userError != nil || user.Region == "" {
lid = defaultLocation
} else {
var locErr error
lid, locErr = locmod.GetLocationIDByCity(user.Region)
if locErr != nil || lid.IsZero() {
lid = defaultLocation
}
}
}
}
//为了不影响用户上传体验,对于sourceID为"0000..."情况的视频,允许上传成功,但默认为审核不通过
var status int
sourceID := req.SourceID
oid, oidErr := primitive.ObjectIDFromHex(req.SourceID)
if (req.NewsType == vidmod.SP || req.NewsType == vidmod.SHORT) && (oidErr != nil || oid.IsZero()) {
oid = primitive.NewObjectID()
sourceID = oid.Hex()
status = 2
}
series := []string{}
if len(req.SeriesCover) != 0 {
series = req.SeriesCover
}
//视频高度不能低于640
var width, height int
resolutions := strings.Split(req.Resolution, "*")
if len(resolutions) >= 2 {
width, _ = strconv.Atoi(resolutions[0])
height, _ = strconv.Atoi(resolutions[1])
}
if width > 0 && height > 0 && height < 640 {
return stderr.ResolutionNotEnough, primitive.NilObjectID
}
ok, err := vidmod.IsSubmitByPublisherID(uid)
if err != nil {
return stderr.ErrDbQueryError, primitive.NilObjectID
}
if !ok {
appg.Redis.Incr("creatorNumber")
}
v := vidmod.VideoModel{
ID: oid,
PublisherID: uid,
NewsType: req.NewsType,
Title: req.Title,
Content: req.Content,
Tags: tags,
SourceID: sourceID,
SourceURL: req.SourceURL,
MimeType: req.MimeType,
Activity: []primitive.ObjectID{},
Filename: req.Filename,
PlayTime: req.PlayTime,
Cover: req.Cover,
CoverThumb: req.CoverThumb,
SeriesCover: series,
SeriesNum: len(series),
Via: req.Via,
Rating: 0,
PlayCount: 0,
LikeCount: 0,
CommentCount: 0,
ShareCount: 0,
Status: status,
Location: lid,
FreeTime: req.FreeTime,
Coins: req.Coins,
Size: req.Size,
Resolution: req.Resolution,
Ratio: req.Ratio,
MD5: req.MD5,
Actor: req.Actor,
IsHideLocation: isHideLocation,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
if req.IsActivity {
videoActivity, err := videoactivitymod.QueryDocumentByStatus(videoactivitymod.Enable)
if err != nil {
return stderr.ErrDbQueryError, primitive.NilObjectID
}
v.ActivityID = videoActivity.ID
}
if v.PlayTime > 0 || v.NewsType == vidmod.SP {
if v.PlayTime < 300 {
v.NewsType = vidmod.SHORT
} else {
v.NewsType = vidmod.SP
}
}
if vid, err = vidmod.InsertBase(v); err != nil {
if mongo.IsDuplicateKeyError(err) {
return stderr.ErrUploadExistVideo, primitive.NilObjectID
}
return stderr.ErrNetWorkBusy, primitive.NilObjectID
}
return stderr.Success, vid
}
// PlayRecord 用户播放行为记录
// 视频播放、标签播放、地理位置播放++
// 播放日志记录
// 播放率统计
func PlayRecord(uid uint64, req vidmod.PlayReq) (code stderr.Code, data interface{}) {
oid, err := primitive.ObjectIDFromHex(req.VideoID)
if err != nil {
log.Error("PlayRecord error", log.Any("uid", uid), log.Any("req", req), log.E(err))
return stderr.ErrParamError, nil
}
var tid primitive.ObjectID
if len(req.TagID) != 0 {
tid, err = primitive.ObjectIDFromHex(req.TagID)
if err != nil {
log.Error("PlayRecord tid error", log.Any("uid", uid), log.Any("req", req), log.E(err))
return stderr.ErrParamError, nil
}
}
now := time.Now()
common.Go(func() {
if req.Publisher <= 0 {
return
}
weektop.Incr(topser.TypeRecommendUser, fmt.Sprintf("%v", req.Publisher), 1)
})
common.Go(func() {
ok, err := userwatchrecordmod.IsExist(uid, oid)
if err != nil {
return
}
if ok {
if err = userwatchrecordmod.Refresh(uid, oid); err != nil {
return
}
} else {
if err = userwatchrecordmod.Insert(uid, oid); err != nil {
return
}
}
p := playlgmod.PlayLog{
UID: uid,
VideoID: oid,
Longer: req.Longer,
Progress: req.Progress,
Via: req.Via,
PlayWay: req.PlayWay,
TagID: tid,
Publisher: req.Publisher,
CreatedAt: now,
}
_ = playlgmod.InsertPlayRecord(p)
isValid := req.Longer >= 5
_ = vidhelpser.IncreaseTag(req.VideoID, tid, isValid)
popCfg, err := vidpopmod.FindVidPopCfg()
if err != nil {
log.Error("vidpopmod.FindVidPopCfg", log.E(err))
}
_ = vidmod.IncPlayCount(oid, req, popCfg)
})
common.Go(func() {
_ = setHotPubliser(now, req.Publisher)
})
return stderr.Success, nil
}
func setHotPubliser(d time.Time, publishserUID uint64) error {
d = timeutil.BeginningOfDay(d)
reidsKey := redisconst.PlayLeaderboardKey("publiser", d.Unix())
_, err := appg.Redis.ZIncrBy(reidsKey, 1, strconv.FormatUint(publishserUID, 10))
return err
}
// SendBaseInfo2FS 上传基本文件信息到aws
func SendBaseInfo2FS(req vidmod.SubmitReq) (code stderr.Code) {
info := vidmod.BaseVid4Aws{
ID: req.SourceID,
CheckSum: req.MD5,
Title: req.Title,
Actors: []string{req.Actor},
AddedTime: time.Now().Format("2006-01-02"),
PlayTime: uint64(req.PlayTime),
Size: req.Size,
Via: "q1",
Filename: req.Filename,
Type: "sp",
}
var tags []string
reqTagsLen := len(req.Tags)
if reqTagsLen > 0 {
tags = make([]string, 0, reqTagsLen)
for _, v := range req.Tags {
tagID, err := primitive.ObjectIDFromHex(v)
if err != nil {
log.Warn("SendBaseInfo2FS error", log.Any("req", req), log.E(err))
continue
}
tagData, err := tagmod.FindOneTagByID(tagID)
if err != nil {
continue
}
tags = append(tags, tagData.TagName)
}
}
info.Tags = tags
c, cancle := context.WithTimeout(context.Background(), timeout)
defer cancle()
params, _ := common.ToJsonM(info)
respBody := commod.Resp{}
httpStatus, err := httputil.DefaultClientPostJsonWithRespWithCtx(c, &respBody, appg.Conf.URL.FileInfoUrl, nil, params)
log.Info("http method SendBaseInfo2FS response code ==>", log.Any("httpStatus", httpStatus), log.Any("respCode", respBody.Code))
if err != nil {
log.Error("SendBaseInfo2FS POSTJsonWithJResp error", log.Any("req", req), log.E(err))
return stderr.ErrConnectToFs
}
if respBody.Code != http.StatusOK {
log.Error("SendBaseInfo2FS status error", log.Any("req", req), log.Any("respBody.Code", respBody.Code), log.E(err))
return stderr.ErrFsServerFile
}
return stderr.Success
}
// SendCoverInfo2FS 上传基本图集信息到aws
func SendCoverInfo2FS(req vidmod.SubmitReq) (code stderr.Code) {
info := vidmod.CoverInfo4Aws{
Type: "SP",
Title: req.Title,
Tags: req.Tags,
CoverImg: req.Cover,
SeriesCover: req.SeriesCover,
Status: "",
MimeType: req.MimeType,
Via: "q1",
Topic: []string{},
NewUpdateAt: time.Now().Format("2006-01-02"),
Desc: "",
}
var tags []string
reqTagsLen := len(req.Tags)
if reqTagsLen > 0 {
tags = make([]string, 0, reqTagsLen)
for _, v := range req.Tags {
tagID, err := primitive.ObjectIDFromHex(v)
if err != nil {
log.Warn("SendCoverInfo2FS error", log.Any("req", req), log.E(err))
continue
}
tagData, err := tagmod.FindOneTagByID(tagID)
if err != nil {
continue
}
tags = append(tags, tagData.TagName)
}
}
info.Tags = tags
c, cancle := context.WithTimeout(context.Background(), timeout)
defer cancle()
params, _ := common.ToJsonM(info)
respBody := commod.Resp{}
httpStatus, err := httputil.DefaultClientPostJsonWithRespWithCtx(c, &respBody, appg.Conf.URL.CoverInfoUrl, nil, params)
log.Info("http method SendCoverInfo2FS response code ==>", log.Any("httpStatus", httpStatus), log.Any("respCode", respBody.Code))
if err != nil {
log.Error("SendCoverInfo2FS POSTJsonWithJResp error", log.Any("req", req), log.E(err))
return stderr.ErrConnectToFs
}
if respBody.Code != http.StatusOK {
log.Error("SendCoverInfo2FS status error", log.Any("req", req), log.Any("respBody.Code", respBody.Code), log.E(err))
return stderr.ErrFsServerFile
}
return stderr.Success
}
// GetLocation 通过id获取地理位置
func GetLocation(uid uint64, id string) (code stderr.Code, data interface{}) {
oid, err := primitive.ObjectIDFromHex(id)
if err != nil {
log.Error("GetLocation error", log.Any("uid", uid), log.Any("id", id), log.E(err))
return stderr.ErrParamError, nil
}
l, err := locmod.GetLocationInfoByID(oid)
if err != nil {
return stderr.ErrDbQueryError, nil
}
if err = locmod.IncVisit(oid); err != nil {
return stderr.ErrDbUpdateError, nil
}
return stderr.Success, vidmod.LocResp{LocInfo: vidmod.LocInfo{
ID: l.ID,
City: l.City,
Cover: l.Cover,
Visit: l.FakeVisit,
CreatedAt: l.CreatedAt,
}}
}
// 总数固定10次 看完之后不再恢复
func GetWatchCount(uid uint64, oid primitive.ObjectID, first string) (isCan bool, watchCount uint64) {
totalCount := TotalFreeWatchCount()
var playWay uint64 = 0
duration := now.BeginningOfDay()
u, err := usermod.FindUserByUID(uid)
if u == nil || err != nil {
isCan = false
return
}
//是否是vip VIP可免费观看
isFree := IsFreeWatch(uid)
if isFree {
isCan = true
return
}
info, err := vidmod.GetVideoInfo(oid.Hex())
if err != nil {
return false, u.WatchCount
}
//判断是否是自己的视频或免费的视频或收费的视频
if info.PublisherID == uid || info.FreeArea || info.Coins > 0 {
return true, u.WatchCount
}
//当前视频在当天是否观看过 如果观看过 则可以继续观看
result := useractmod.IsViewTodayByNoVip(uid, oid, duration)
if result {
isCan = true
return
}
watchCount = u.WatchCount
if watchCount > totalCount {
watchCount = totalCount
}
//当观看次数为0时 分两种情况
if watchCount == 0 {
//判断当天是否观看过 如果没有观看过 则设置为10
// cnt := useractmod.ActCount(uid, duration)
// if cnt == 0 {
// watchCount = vidmod.TotalWatchCount
// _, _ = usermod.UpdateUserWatchCount(uid, int64(vidmod.TotalWatchCount))
// }
isCan = false
}
if 0 < watchCount && watchCount <= totalCount {
isCan = true
if first == h5FirstPlay {
return isCan, watchCount
}
_ = useractmod.UserActInsert(useractmod.UserAct{
VID: oid,
PlayWay: playWay,
UID: uid,
CreatedAt: time.Now(),
DailyDate: duration,
})
wCount := int64(watchCount - 1)
_, _ = usermod.UpdateUserWatchCount(uid, wCount)
}
return isCan, watchCount
}
// TotalFreeWatchCount 返回系统配置的免费观看总次数。
func TotalFreeWatchCount() uint64 {
return sys_config.GetTotalWatchCount()
}
func FreshCount(uid uint64) uint64 {
u, err := usermod.FindUserByUID(uid)
if u == nil || err != nil {
return 0
}
return u.WatchCount
}
func IsFreeWatch(uid uint64) bool {
u, err := usermod.FindUserByUID(uid)
if err != nil || u == nil {
return false
}
return !u.VipExpireDate.Before(time.Now())
}
func GetHotList(uid uint64, skip, limit int64) (list interface{}, hasNext bool, err error) {
opt := (&searcher.Option{}).
SetSkip(skip).
SetLimit(limit)
hotResult, err := vidhotsearcher.NewVidHotSearcher(uid).Search(nil, opt)
if err != nil {
return nil, false, err
}
return hotResult.Data(), hotResult.HasNext(), nil
}
func GetHotKWList(uid uint64, skip, limit int64) (list interface{}, hasNext bool, err error) {
//每个关键字平均取视屏
//如:需要12个视屏 用6个关键字搜素 每个关键字最多查询2个视屏
const keywordCount = 6
vidLimitPer := limit / keywordCount
vidOpt := (&searcher.Option{}).
SetSkip(limit * vidLimitPer).
SetLimit(limit)
hkwOpt := (&searcher.Option{}).SetLimit(keywordCount) //搜索排名前keywordCount的关键字
hkwResult, err := vidhkwsearcher.NewVidHKWSearcher(uid, vidOpt).Search(nil, hkwOpt)
if err != nil {
return nil, false, err
}
if hkwResult.Count() >= limit { //不用补充视屏
return hkwResult.Data(), hkwResult.HasNext(), nil
}
//热搜视屏代替
hotOpt := (&searcher.Option{}).
SetSkip(skip).
SetLimit(limit)
hotResult, err := vidhotsearcher.NewVidHotSearcher(uid).Search(nil, hotOpt)
if err != nil {
return nil, false, err
}
return hotResult.Data(), hotResult.HasNext(), nil
}
// DeleteManyByTTL 删除自己的视频 被删除的视屏归属为officialUID,未审核或审核未通过的视屏通过TTL删除,其余视屏保留
func DeleteManyByTTL(uid uint64, ids ...primitive.ObjectID) (code stderr.Code, data interface{}) {
//redis清除视频id
mats := []vidmod.Matcher{
(&vidmod.PublisherIDMatch{PublisherID: &uid}).New(),
(&vidmod.IDInMatch{IDs: ids}).New(),
}
list, err := vidmod.FindMany(mats...)
if err != nil {
return stderr.ErrDbQueryError, nil
}
//removeIDs-转移到官方账号 并置为审核不通过
//fakeRemoveIDs-审核通过的金币视频,如果有人购买,做逻辑删除
//deleteIDs-物理删除
listLen := len(list)
removeIDs, fakeRemoveIDs, deleteIDs := make([]primitive.ObjectID, 0, listLen), make([]primitive.ObjectID, 0, listLen), make([]primitive.ObjectID, 0, listLen)
for _, v := range list {
if v.Status == 1 || v.Status == 3 { //状态,0 未审核 1通过 2审核失败 3视为免费 默认为0
if v.Status == 1 && v.Coins > 0 {
isBePay := payvidlgmod.IsVidIfBePay(v.ID) //判断是否当前视频是否购买过
if isBePay {
fakeRemoveIDs = append(fakeRemoveIDs, v.ID)
} else {
removeIDs = append(removeIDs, v.ID)
}
} else {
removeIDs = append(removeIDs, v.ID)
}
}
if v.Status == 0 || v.Status == 2 { //状态,0 未审核 1通过 2审核失败 3视为免费 默认为0
deleteIDs = append(deleteIDs, v.ID)
}
}
rmSetDoc := (&vidmod.SetDoc{}).SetPublisherID(constant.DeleteUID).SetStatus(vidmod.CheckFailure)
logicRmSetDoc := (&vidmod.SetDoc{}).SetPublisherID(constant.DeleteUID).SetStatus(vidmod.IsDeleted)
if _, err = vidmod.ModifyMany(removeIDs, *rmSetDoc); err != nil {
return stderr.ErrDbUpdateError, nil
}
if _, err = vidmod.ModifyMany(fakeRemoveIDs, *logicRmSetDoc); err != nil {
return stderr.ErrDbUpdateError, nil
}
if len(deleteIDs) != 0 {
_ = vidmod.DelVideo(deleteIDs)
}
return stderr.Success, nil
}
// GetNewestNewsList 获取最新帖子
func GetNewestNewsList(uid, page, size uint64, head []*vidmod.VideoModel, reqDate time.Time) (
code stderr.Code, data interface{}) {
infos, hasNext, err := vidhelpser.GetNewestNews(page, size, reqDate)
if err != nil {
return stderr.ErrDbQueryError, nil
}
if len(head) > 0 {
head = append(head, infos...)
head = RemoveRep(head)
} else {
head = infos
}
newInfos := vidhelpser.EncodeVideoInfo(uid, head)
data = vidmod.NewsListResp{List: newInfos, HasNext: hasNext}
return stderr.Success, data
}
// GetNewestNewsList 获取最新帖子
func GetNewestNewsList_old(uid, page, size uint64, head []*vidmod.VideoModel, subType int) (stderr.Code, interface{}) {
var infos []*vidmod.VideoModel
var hasNext bool
var err error
if subType == 1 { // 1 只取短视频
infos, hasNext, err = vidhelpser.GetNewestShortVideo(page, size)
if err != nil {
return stderr.ErrDbQueryError, nil
}
} else {
infos, hasNext, err = vidhelpser.GetNewestNews_old(page, size)
if err != nil {
return stderr.ErrDbQueryError, nil
}
}
if len(head) > 0 {
head = append(head, infos...)
head = RemoveRep(head)
} else {
head = infos
}
return stderr.Success, vidmod.NewsListResp{List: vidhelpser.EncodeVideoInfo(uid, head), HasNext: hasNext}
}
// GetHotestNewsList 获取最热帖子
func GetHotestNewsList(uid uint64, subType int, page, size uint64) (code stderr.Code, data interface{}) {
//优先获取撩吧推荐置顶的
top := make([]*vidmod.VideoModel, 0)
if page == 1 {
var err error
top, err = vidmod.GetLiaoBaTop()
if err != nil {
return stderr.ErrDbQueryError, nil
}
size = size - uint64(len(top))
}
//置顶排序
sort.Sort(vidmod.VideoModelSort(top))
var ids []primitive.ObjectID
hasNext := false
var err error
if subType == vidmod.TodayRank {
ids, hasNext, err = hotstatmod.GetHotValueToday(page, size)
}
if subType == vidmod.WeekRank {
ids, hasNext, err = hotstatmod.GetHotValueWeek(page, size)
}
if subType == vidmod.MonthRank {
ids, hasNext, err = hotstatmod.GetHotValueMonth(page, size)
}
if err != nil {
return stderr.ErrDbQueryError, nil
}
mInfos, err := vidmod.GetNewsByIDs2M(ids)
if err != nil {
return stderr.ErrDbQueryError, nil
}
topLen := len(top)
sortInfos := make([]*vidmod.VideoModel, 0, len(ids)+topLen)
if topLen > 0 {
sortInfos = append(sortInfos, top...)
}
for _, v := range ids {
if mInfos[v] != nil {
sortInfos = append(sortInfos, mInfos[v])
}
}
newInfos := vidhelpser.EncodeVideoInfo(uid, sortInfos)
data = vidmod.NewsListResp{List: newInfos, HasNext: hasNext}
return stderr.Success, data
}
// DoUnlikeVideo 不感兴趣
func DoUnlikeVideo(uid uint64, vid string) (code stderr.Code, data interface{}) {
oid, err := primitive.ObjectIDFromHex(vid)
if err != nil {
return stderr.ErrParamError, nil
}
fmt.Println(oid)
return stderr.Success, nil
}
// GetNewsCoinsList 金币专区
func GetNewsCoinsList(uid uint64, subType int, page, size uint64, head []*vidmod.VideoModel) (stderr.Code, interface{}) {
infos, hasNext, err := vidmod.GetCoinsNews(page, size, subType)
if err != nil {
return stderr.ErrDbQueryError, nil
}
if len(head) > 0 {
head = append(head, infos...)
head = RemoveRep(head)
} else {
head = infos
}
return stderr.Success, vidmod.NewsListResp{List: vidhelpser.EncodeVideoInfo(uid, head), HasNext: hasNext}
}
// GetOriginalList 新版原创专区
func GetOriginalList(uid uint64, subType int, page, size uint64) (code stderr.Code, data interface{}) {
/** TODO:
1.获取博主排序
2.获取博主前三则视频
*/
users, hasNext, err := usermod.OfficialCertList(page, size, subType)
if err != nil {
return stderr.ErrDbQueryError, err
}
var (
lids []primitive.ObjectID
vids []primitive.ObjectID
wg sync.WaitGroup
m sync.Map
self *usermod.User
mLocation = make(map[primitive.ObjectID]locmod.Location)
mFree = make(map[uint64]bool)
videoDiscLog videodiscountmod.VideoDiscountLog
now = time.Now()
largestDiscount int
)
wg.Add(len(users))
for _, v := range users {
go func(u *usermod.User) {
defer wg.Done()
infos, err := GetUserTopWork(u.UID)
if err != nil {
return
}
if len(infos) == 0 {
return
}
var lidOk bool
vidInfos := make([]*vidmod.VideoInfo, len(infos))
for i, v := range infos {
if !lidOk {
lids = append(lids, v.Location)
lidOk = true
}
vids = append(vids, v.ID)
newType := vidmod.SP
if len(v.NewsType) != 0 {
newType = v.NewsType
}
vidInfos[i] = &vidmod.VideoInfo{
VideoBase: vidmod.VideoBase{
ID: v.ID,
NewsType: newType,
Title: v.Title,
PlayTime: v.PlayTime,
Cover: v.Cover,
CoverThumb: v.CoverThumb,
SeriesCover: v.SeriesCover,
PlayCount: v.FakePlayCount,
LikeCount: v.FakeLikeCount,
CommentCount: v.CommentCount,
ShareCount: v.FakeShareCount,
Coins: v.Coins,
Size: v.Size,
Resolution: v.Resolution,
Ratio: v.Ratio,
PurchaseCount: v.PurchaseCount,
CreatedAt: v.CreatedAt,
ReviewAt: v.ReviewAt,
FreeTime: v.FreeTime,
Status: v.Status,
Reason: v.Reason,
SourceURL: v.SourceURL,
H265Url: vidhelpser.H265URLForApp(v),
LinkUrl: v.LinkUrl,
IsHideLocation: v.IsHideLocation,
FreeArea: v.FreeArea,
IsTopping: v.IsTopping,
IsChoosen: v.IsChoosen,
IsRecommend: v.IsRecommend,
Rewarded: v.Rewarded.Add(v.FakeRewarded),
OriginCoins: v.Coins,
},
UInfo: vidmod.Publisher{
UInfo: usermod.BaseInfoVip{
UID: u.UID,
Name: u.Name,
Gender: u.Gender,
Portrait: u.Portrait,
HasLocked: u.HasLocked,
HasBanned: u.HasBanned,
VipLevel: u.VipLevel,
IsVip: u.IsVIP(time.Now()),
SuperUser: u.SuperUser,
ActiveValue: u.ActiveValue,
OfficialCert: u.OfficialCert,
Age: u.Age(),
},
},
Location: vidmod.LocInfo{ID: v.Location},
}
}
m.Store(u.UID, vidInfos)
}(v)
}
wg.Wait()
wg.Add(4)
common.Go(func() {
defer wg.Done()
self, err = usermod.FindUserByUID(uid)
if err != nil {
log.Error("usermod.FindUserByUID", log.Any("uid", uid), log.E(err))
}
if !self.IsVIP(now) {
largestDiscount, err = productmod.GetLargestDiscountVIPCard()
if err != nil {
log.Error("GetLargestDiscountVIPCard", log.Any("uid", uid), log.E(err))
}
}
})
common.Go(func() {
defer wg.Done()
// 查询地理位置
mLocation, err = locmod.GetLocationInfoByIDs(lids)
if err != nil {
log.Error("usermod.FindUserByUID", 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("videodiscountmod.GetByUID", log.Any("uid", uid), log.E(err))
}
})
wg.Wait()
if err != nil {
return stderr.ErrDbQueryError, err
}
//查询购买信息
mPay, _ := payvidlgmod.IsPay4Videos(uid, vids)
// 排序
out := make([][]*vidmod.VideoInfo, len(users))
freeTrialContext := vidhelpser.FreeTrialBadgeContextForUser(uid, self)
for i, v := range users {
if infos, ok := m.Load(v.UID); ok {
for i, info := range infos.([]*vidmod.VideoInfo) {
if item, ok := mLocation[info.Location.ID]; ok {
info.Location = vidmod.LocInfo{
ID: item.ID,
City: item.City,
Cover: item.Cover,
Visit: item.Visit,
CreatedAt: item.CreatedAt,
}
}
//是否购买
infos.([]*vidmod.VideoInfo)[i].VidStatus = vidmod.VideoStatus{
HasPaid: mPay[info.ID],
}
//计算折扣后视频金币数
infos.([]*vidmod.VideoInfo)[i].Coins = vidhelpser.CalcVideoCoins(info.Coins, v.UID, self, mFree,
videoDiscLog, largestDiscount, nil)
}
vidhelpser.ApplyFreeTrialBadgeToVideoInfos(freeTrialContext, infos.([]*vidmod.VideoInfo))
out[i] = infos.([]*vidmod.VideoInfo)
}
}
return stderr.Success, vidmod.OriginalInfo{List: out, HasNext: hasNext, Version: vidmod.Version}
}
// GetHeadNews 获取置顶、力荐、加精
func GetHeadNews(typ int, lid primitive.ObjectID, isMadou bool) ([]*vidmod.VideoModel, error) {
topping := []*vidmod.VideoModel{}
recomm := []*vidmod.VideoModel{}
chosen := []*vidmod.VideoModel{}
var wg sync.WaitGroup
wg.Add(3)
common.Go(func() {
defer wg.Done()
topping, _ = vidmod.GetTopping(typ, lid, isMadou)
})
common.Go(func() {
defer wg.Done()
recomm, _ = vidmod.GetReco(typ, lid, isMadou)
})
common.Go(func() {
defer wg.Done()
chosen, _ = vidmod.GetChosen(typ, lid, isMadou)
})
wg.Wait()
toppingLen := len(topping)
recommLen := len(recomm)
chosenLen := len(chosen)
head := make([]*vidmod.VideoModel, toppingLen, toppingLen+recommLen+chosenLen)
mFlag := make(map[primitive.ObjectID]bool)
for i, v := range topping {
mFlag[v.ID] = true
head[i] = v
}
for _, v := range recomm {
if !mFlag[v.ID] {
mFlag[v.ID] = true
head = append(head, v)
}
}
for _, v := range chosen {
if !mFlag[v.ID] {
mFlag[v.ID] = true
head = append(head, v)
}
}
return head, nil
}
func GetVIPVideo(uid, page, size uint64) (stderr.Code, interface{}) {
infos, hasNext, err := vidmod.GetVipList(page, size)
if err != nil {
return stderr.ErrDbQueryError, nil
}
newInfos := vidhelpser.EncodeVideoInfo(uid, infos)
data := vidmod.NewsListResp{List: newInfos, HasNext: hasNext}
return stderr.Success, data
}
func GetVideosUnderSubModuleNew(subModuleID primitive.ObjectID, uid uint64, req VideoUnderSubModuleReq) (data *VideoUnderSubModuleResp, err error) {
defer func() {
applyFreeTrialBadgeToSubModule(uid, data)
}()
ret := VideoUnderSubModuleResp{
HasNext: false,
AllVideoInfo: []*vidmod.VideoInfoResp{},
AllSection: []Section{},
}
subModule, err := moduleconfmod.GetByID(subModuleID)
if err != nil {
return &ret, err
}
if subModule.ID.IsZero() {
return &ret, err
}
if !subModule.IsActiveAt(time.Now()) {
return &ret, nil
}
switch subModule.Type {
case moduleconfmod.HomePage:
cachev2.Classes().CacheTime(time.Minute*5).AutoListKey("91PORN-HomePage").ResBind(&ret).Cache(HandleHomePage, subModule, req)
case moduleconfmod.DeepWeb:
cachev2.Classes().CacheTime(time.Minute*5).AutoListKey("91PORN-DeepWeb").ResBind(&ret).Cache(HandleHaijiaoList, subModule, req)
case moduleconfmod.Cartoon, moduleconfmod.Comics, moduleconfmod.Novel:
cachev2.Classes().CacheTime(time.Minute*5).AutoListKey("91PORN-ACG").ResBind(&ret).Cache(HandleAcg, subModule, req)
case moduleconfmod.Community: //, moduleconfmod.PrivateCircle:
cachev2.Classes().CacheTime(time.Minute*5).AutoListKey("91PORN-Community").ResBind(&ret).Cache(HandleCommunity, subModule, req)
case moduleconfmod.Game, moduleconfmod.Pics:
cachev2.Classes().CacheTime(time.Minute*5).AutoListKey("91PORN-CommunityPicGame").ResBind(&ret).Cache(HandleCommunityPICAndGame, subModule, req)
default:
return
}
return &ret, nil
}
func HandleAcg(subModule moduleconfmod.ModuleConf, req VideoUnderSubModuleReq) (data VideoUnderSubModuleResp) {
switch subModule.ShowType {
case moduleconfmod.AllSectionShowType:
// 专题样式
data = HandleAcgSectionList(subModule, req)
default:
// 海角样式
data = HandleAcgHaiJiao(subModule, req)
}
return
}
func HandleAcgHaiJiao(subModule moduleconfmod.ModuleConf, req VideoUnderSubModuleReq) (data VideoUnderSubModuleResp) {
if subModule.Type == moduleconfmod.Cartoon || subModule.Type == moduleconfmod.Comics {
allSection, err := modulesectionmod.GetAllBySubModuleID(subModule.ID)
if err != nil {
return
}
for _, s := range allSection {
sec := Section{
SectionID: s.ID.Hex(),
SectionName: s.SectionName,
Hot: s.Hot,
}
if s.SectionCover != nil {
sec.SectionCover = *s.SectionCover
}
data.AllSection = append(data.AllSection, sec)
}
}
topIds := []primitive.ObjectID{}
topList := []*mediamod.Media{}
var err error
// 判断当前排序规则是否存在置顶排序
for _, v := range subModule.HaiJiaoStyle.SortRules {
if v.Val == commod.SortType(req.ModuleSort) && v.Top == true {
// 通过模块id获取acg动漫列表
topList, _, err = mediamod.GetList(bson.M{
"mId": subModule.ID,
"sortCode": bson.M{"$gt": 0},
}, 0, 10, bson.D{
{Key: "sortCode", Value: -1},
})
if err != nil {
log.Error(fmt.Sprintf("HandleAcgHaiJiao mediamod.GetList err:%v", err))
return
}
for _, vi := range topList {
topIds = append(topIds, vi.ID)
}
break
}
}
filter := bson.M{"mId": subModule.ID, "isDelete": false, "status": 1}
if len(topIds) > 0 {
filter["_id"] = bson.M{"$nin": topIds}
}
sort := acgMediaSort(req.ModuleSort)
switch commod.SortType(req.ModuleSort) {
case commod.New:
// 最新排序已由 acgMediaSort 统一处理。
case commod.MostHot:
// 最热(热门推荐)
sort = bson.D{{Key: "countLike", Value: -1}, {"createdAt", -1}}
case commod.MostWatch: // 最多观看
sort = bson.D{{Key: "countBrowse", Value: -1}, {"createdAt", -1}}
case commod.MostCollect: // 最多收藏
sort = bson.D{{"countCollect", -1}, {"createdAt", -1}}
case commod.HotComment: // 热评
sort = bson.D{{Key: "hot", Value: -1}, {"createdAt", -1}}
default:
// 最新
sort = bson.D{{"createdAt", -1}}
}
// 通过模块id获取acg动漫列表
list, hasNext, err := mediamod.GetList(filter, int64(req.Skip()), int64(req.Limit()), sort)
if err != nil {
log.Error(fmt.Sprintf("HandleAcgHaiJiao mediamod.GetList err:%v", err))
return
}
if req.PageNumber == 1 && len(topList) > 0 {
list = append(topList, list...)
}
data.HasNext = hasNext
data.AllMediaInfo = mediaser.FillNotStatusMedias(list, false)
return data
}
func acgMediaSort(moduleSort int) bson.D {
if commod.SortType(moduleSort) == commod.New {
return bson.D{
{Key: "latestPublishedAt", Value: -1},
{Key: "contentUpdateTime", Value: -1},
{Key: "createdAt", Value: -1},
{Key: "_id", Value: -1},
}
}
return bson.D{{Key: "createdAt", Value: -1}}
}
func HandleAcgSectionList(subModule moduleconfmod.ModuleConf, req VideoUnderSubModuleReq) (resp VideoUnderSubModuleResp) {
page := commod.Page{
PageNumber: req.PageNumber,
PageSize: req.PageSize,
Sort: []commod.Sorts{
{SortKey: "sort", SortVal: -1},
{SortKey: "createdAt", SortVal: -1},
},
}
sections, hasNext, err := modulesectionmod.GetBySubModuleID(subModule.ID, page)
if err != nil {
log.Error(fmt.Sprintf("HandleCartoonSingleSection modulesectionmod.GetBySubModuleID err:%v", err))
return resp
}
if len(sections) < 0 {
return resp
}
resp.HasNext = hasNext
wg := &sync.WaitGroup{}
sectionResult := make(chan Section, len(sections))
for _, v := range sections {
section := v
// 如果是猜你喜欢专题(ACG不存在这个,直接忽略)
if section.ShowType == modulesectionmod.GuessYouLike {
continue
}
wg.Add(1)
common.Go(func() {
defer wg.Done()
num, ok := modulesectionmod.ACGShowTypeHashVale[section.ShowType]
// 兼容旧版本找不到新类型的情况
if !ok {
// 默认 一大四小
num = modulesectionmod.ACGShowTypeHashVale[modulesectionmod.FourGrid]
section.ShowType = modulesectionmod.FourGrid
}
secVidMap := Section{
SectionID: section.ID.Hex(),
SectionTitle: section.SectionTitle,
ShowType: section.ShowType,
SectionName: section.SectionName,
}
if section.Sort != nil {
secVidMap.Sort = *section.Sort
}
if section.SectionCover != nil {
secVidMap.SectionCover = *section.SectionCover
}
//op := options.Find().SetLimit(int64(num)).SetSort(bson.D{{Key: "sortCode", Value: -1}, {Key: "createdAt", Value: -1}})
op := options.Find().SetLimit(int64(num)).SetSort(bson.D{{Key: "sectionSort", Value: -1}, {Key: "createdAt", Value: -1}})
mediaBases, err := mediamod.SectionVideosBySectionID(section.ID, op)
if err != nil {
log.Error(fmt.Sprintf("mediamod SectionVideos By SectionID:%v, err:%v", section.ID, err))
sectionResult <- secVidMap
return
}
if len(mediaBases) == 0 {
log.Warn("mediamod.SectionVideos no video info")
sectionResult <- secVidMap
return
}
secVidMap.AllMediaInfo = mediaser.FillNotStatusMedias(mediaBases, false)
sectionResult <- secVidMap
})
}
wg.Wait()
close(sectionResult)
for section := range sectionResult {
resp.AllSection = append(resp.AllSection, section)
}
sort.Slice(resp.AllSection, func(i, j int) bool {
// 正序排
return resp.AllSection[i].Sort < resp.AllSection[j].Sort
})
return resp
}
func HandleHomePage(subModule moduleconfmod.ModuleConf, req VideoUnderSubModuleReq) (data VideoUnderSubModuleResp) {
switch subModule.ShowType {
//case moduleconfmod.NewPageShowType:
// // 首页最新/推荐展示类型
// return HandleHomePageNew(subModule, req)
case moduleconfmod.HJShowType:
// 海角系 专题+列表
return HandleHaijiaoList(subModule, req)
case moduleconfmod.AllSectionShowType:
// 专题展示样式
return HandleSectionList(subModule, req)
default:
return HandleHaijiaoList(subModule, req)
}
return
}
func HandleHaijiaoList(subModule moduleconfmod.ModuleConf, req VideoUnderSubModuleReq) (data VideoUnderSubModuleResp) {
var (
videos []*vidmod.VideoModel
topVideos []*vidmod.VideoInfoResp
chosenVideos []*vidmod.VideoInfoResp
)
// 展示专题
if subModule.HaiJiaoStyle.SectionStyle > 0 {
allSection, err := modulesectionmod.HomePageGetBySubModuleID(subModule.ID)
if err != nil {
return
}
for _, s := range allSection {
sec := Section{
SectionID: s.ID.Hex(),
SectionName: s.SectionName,
Hot: s.Hot,
}
if s.SectionCover != nil {
sec.SectionCover = *s.SectionCover
}
data.AllSection = append(data.AllSection, sec)
}
}
wg := &sync.WaitGroup{}
if subModule.HaiJiaoStyle.ShowChosenVideo == 1 {
wg.Add(1)
common.Go(func() {
defer wg.Done()
// 获取推荐的数据
filter := bson.M{"mId": subModule.ID.Hex(), "newsType": vidmod.SP, "status": vidmod.CheckPass, "chosen": true}
sort := bson.D{{"isTopping", -1}, {"chosenDate", -1}}
opt := options.Find().
SetSort(sort).
SetLimit(20).
SetProjection(bson.M{"richText": 0})
// 首页精选视频
list, err := vidmod.GetVideoListByCond(filter, opt) // 获取视频详情
if err != nil {
log.Error(fmt.Sprintf("HandleAllList vidmod.GetVideoListByCond err:%v", err))
return
}
chosenVideos = vidhelpser.NewEncodeVideoInfoNotStatus(list)
})
}
topIds := []primitive.ObjectID{}
// 判断当前排序规则是否存在置顶排序
for _, v := range subModule.HaiJiaoStyle.SortRules {
if v.Val == commod.SortType(req.ModuleSort) && v.Top == true {
// 获取置顶数据
wg.Add(1)
common.Go(func() {
defer wg.Done()
// 获取置顶的数据
filter := bson.M{"mId": subModule.ID.Hex(), "liaoBaTopSort": bson.M{"$gt": 0}, "chosen": false}
sort := bson.D{{"liaoBaTopSort", -1}}
opt := options.Find().
SetSort(sort).
SetLimit(10).
SetProjection(bson.M{"richText": 0})
// 首页精选视频
list, err := vidmod.GetVideoListByCond(filter, opt) // 获取视频详情
if err != nil {
log.Error(fmt.Sprintf("HandleAllList vidmod.GetVideoListByCond err:%v", err))
return
}
topVideos = vidhelpser.NewEncodeVideoInfoNotStatus(list)
for _, video := range topVideos {
topIds = append(topIds, video.ID)
}
})
break
}
}
wg.Wait()
// 首页
filter := bson.M{
"mId": subModule.ID.Hex(),
"status": vidmod.CheckPass,
"newsType": vidmod.SP,
"chosen": false,
}
if len(topIds) > 0 {
filter["_id"] = bson.M{"$nin": topIds}
}
sort := bson.D{{"reviewAt", -1}}
switch commod.SortType(req.ModuleSort) {
case commod.New:
// 最新
sort = bson.D{{"reviewAt", -1}}
case commod.MostHot:
// 最热(热门推荐)
sort = bson.D{{"liaoBaTopSort", -1}, {Key: "likeCount", Value: -1}, {"reviewAt", -1}}
case commod.MostWatch:
// 最多观看
sort = bson.D{{Key: "playCount", Value: -1}, {"reviewAt", -1}}
case commod.MostCollect:
// 最多收藏
sort = bson.D{{"collectCount", -1}, {"reviewAt", -1}}
case commod.HotComment: // 热度值
sort = bson.D{{"hot", -1}, {"reviewAt", -1}}
default:
// 最新
sort = bson.D{{"reviewAt", -1}}
}
opt := options.Find().SetSort(sort).SetSkip(req.Skip64()).SetLimit(req.Limit64() + 1).SetProjection(bson.M{"richText": 0})
var err error
videos, err = vidmod.GetVideoListByCond(filter, opt) // 获取视频详情
if err != nil {
log.Error(fmt.Sprintf("vidmod.GetVideoListByCond err:%v", err))
return
}
if len(videos) > int(req.Limit()) {
data.HasNext = true
videos = videos[:req.Limit()]
}
data.AllVideoInfo = vidhelpser.NewEncodeVideoInfoNotStatus(videos)
if req.PageNumber == 1 && len(topVideos) > 0 {
// 第一页,直接插入到最前面
data.AllVideoInfo = append(topVideos, data.AllVideoInfo...)
}
data.ChosenVideoInfo = chosenVideos
return
}
func HandleSectionList(subModule moduleconfmod.ModuleConf, req VideoUnderSubModuleReq) (resp VideoUnderSubModuleResp) {
page := commod.Page{
PageNumber: req.PageNumber,
PageSize: req.PageSize,
Sort: []commod.Sorts{
{SortKey: "sort", SortVal: -1},
{SortKey: "createdAt", SortVal: -1},
},
}
sections, hasNext, err := modulesectionmod.GetBySubModuleID(subModule.ID, page)
if err != nil {
log.Error(fmt.Sprintf("HandleSectionList modulesectionmod.GetBySubModuleID err:%v", err))
return resp
}
if len(sections) < 0 {
return resp
}
resp.HasNext = hasNext
wg := &sync.WaitGroup{}
sectionResult := make(chan Section, len(sections))
for _, v := range sections {
section := v
wg.Add(1)
common.Go(func() {
defer wg.Done()
num := modulesectionmod.ShowTypeHashVale[section.ShowType]
if num == 0 {
num = int(req.PageSize)
}
num = num + 2 // 满足ab测需求,额外多返回2个
secVidMap := Section{
SectionID: section.ID.Hex(),
SectionTitle: section.SectionTitle,
ShowType: section.ShowType,
SectionName: section.SectionName,
Hot: section.Hot,
}
if section.Sort != nil {
secVidMap.Sort = *section.Sort
}
if section.SectionCover != nil {
secVidMap.SectionCover = *section.SectionCover
}
if section.ShowType == modulesectionmod.GuessYouLike {
sectionResult <- secVidMap
return
}
var (
sectionVideos []modulevidmod.SectionVideo
videos []*vidmod.VideoModel
)
opt := options.Find().SetSort(bson.D{{Key: "sortCode", Value: -1}, {Key: "videoReviewedAt", Value: -1}}).SetLimit(int64(num))
sectionVideos, err = modulevidmod.SectionVideosBySectionID(section.ID, opt)
if err != nil {
log.Error("GetSectionVideosBySectionID", log.Any("sectionID", section.ID), log.E(err))
return
}
if len(sectionVideos) == 0 {
log.Warn("modulevidmod.SectionVideosBySectionID empty video list", log.Any("sectionID", section.ID))
return
}
videoIDs := make([]primitive.ObjectID, len(sectionVideos))
for i, v := range sectionVideos {
videoIDs[i] = v.VideoID
}
// 获取视频详情
if videos, err = vidmod.GetVideoListByIDs(videoIDs); err != nil {
log.Error("vidmod.GetVideoListByIDs", log.Any("videoIDs", videoIDs), log.E(err))
return
}
if len(videos) == 0 {
log.Warn("vidmod.GetVideoListByIDs no video info", log.Any("videosIDs", videoIDs))
return
}
// 根据专题中的重新排序
sortVideos := []*vidmod.VideoModel{}
videoMap := make(map[primitive.ObjectID]*vidmod.VideoModel)
for _, video := range videos {
videoMap[video.ID] = video
}
for _, video := range sectionVideos {
vi, ok := videoMap[video.VideoID]
if ok {
sortVideos = append(sortVideos, vi)
}
}
videoList := vidhelpser.NewEncodeVideoInfoNotStatus(sortVideos)
secVidMap.AllVideoInfo = videoList
sectionResult <- secVidMap
})
}
wg.Wait()
close(sectionResult)
for section := range sectionResult {
resp.AllSection = append(resp.AllSection, section)
}
sort.Slice(resp.AllSection, func(i, j int) bool {
// 正序排
return resp.AllSection[i].Sort < resp.AllSection[j].Sort
})
return
}
func HandleCommunity(subModule moduleconfmod.ModuleConf, req VideoUnderSubModuleReq) (data VideoUnderSubModuleResp) {
var (
videos []*vidmod.VideoModel
videoTagIds []primitive.ObjectID
err error
next bool
)
allSection, err := modulesectionmod.GetAllBySubModuleID(subModule.ID)
if err != nil {
return
}
for _, s := range allSection {
var allTag []Tag
if s.Tags != nil && len(*s.Tags) > 0 && s.TagIds != nil && len(*s.TagIds) > 0 {
videoTagIds = append(videoTagIds, *s.TagIds...)
tags, err := tagmod.FindOneTagByIds(*s.TagIds)
if err != nil {
continue
}
if tags != nil && len(tags) > 0 {
for _, t := range tags {
allTag = append(allTag, Tag{
ID: t.ID,
TagName: t.TagName,
CoverImg: t.CoverImg,
VideoCount: t.VideoCount,
HotMark: t.HotMark,
})
}
}
}
sec := Section{
SectionID: s.ID.Hex(),
SectionName: s.SectionName,
AllTags: allTag,
}
if s.SectionCover != nil {
sec.SectionCover = *s.SectionCover
}
data.AllSection = append(data.AllSection, sec)
}
tagId, _ := primitive.ObjectIDFromHex(req.TagId)
if !tagId.IsZero() {
videoTagIds = []primitive.ObjectID{tagId}
}
if len(videoTagIds) == 0 {
return data
}
var (
topVideos []*vidmod.VideoModel
now = time.Now()
filter = bson.M{}
sort = bson.D{}
opt = options.Find().SetSkip(req.Skip64()).SetLimit(req.Limit64()).SetProjection(bson.M{"richText": 0})
)
// 社区置顶,第一页查询置顶数据,没有时间限制
if req.ModuleSort == 7 && req.PageNumber == 1 {
topFilter := bson.M{
"tags": bson.M{"$in": videoTagIds},
"status": vidmod.CheckPass,
"liaoBaTopSort": bson.M{"$gt": 0},
}
topSort := bson.D{
{"liaoBaTopSort", -1},
{"reviewAt", -1},
}
topOpt := options.Find().
SetLimit(10).
SetSort(topSort).
SetProjection(bson.M{"richText": 0})
topVideos, _, err = vidmod.FindList(topFilter, topOpt) // 获取视频详情
if err != nil {
log.Error(fmt.Sprintf("vidmod.FindList get top vid err:%v", err))
return
}
}
//帖子排序
//最新:最新上架的 sortType = 1
//最热:收藏量 10-120天 sortType = 2
//推荐: 收藏量 14天内 sortType = 7
//热评:hot排序 1年内 sortType = 9
filter = bson.M{"tags": bson.M{"$in": videoTagIds}, "status": vidmod.CheckPass}
switch req.ModuleSort {
case 2: // 精选/最多喜欢 // x天内点赞最多的
filter["reviewAt"] = bson.M{"$gte": now.AddDate(0, -4, 0), "$lt": now.AddDate(0, -1, 0)}
sort = bson.D{{"collectCount", -1}, {"reviewAt", -1}}
case 3: // 推荐
filter["reviewAt"] = bson.M{"$gte": now.Add(-time.Hour * 24 * 30)}
sort = bson.D{{"playCount", -1}, {"reviewAt", -1}}
case 6: // 社区视频 x天内观看最多(视频)
filter["reviewAt"] = bson.M{"$gte": now.Add(-time.Hour * 24 * 30)}
filter["newsType"] = vidmod.SP
sort = bson.D{{"playCount", -1}, {"reviewAt", -1}}
case 7: // 推荐/最多收藏 x天内收藏最多
filter["reviewAt"] = bson.M{"$gte": now.Add(-time.Hour * 24 * 14)}
filter["liaoBaTopSort"] = 0
sort = bson.D{{"collectCount", -1}, {"reviewAt", -1}}
case 9: // 热评
filter["reviewAt"] = bson.M{"$gte": now.AddDate(-1, 0, 0)}
sort = bson.D{{"hot", -1}, {"reviewAt", -1}}
case 1: // 最新
fallthrough
default:
sort = bson.D{{"reviewAt", -1}}
}
opt = opt.SetSort(sort)
videos, next, err = vidmod.FindList(filter, opt) // 获取视频详情
if err != nil {
log.Error(fmt.Sprintf("vidmod.GetVideoListByCond err:%v", err))
return
}
if len(topVideos) > 0 {
videos = append(topVideos, videos...)
}
if len(videos) == 0 {
log.Warn(fmt.Sprintf("vidmod.GetVideoListByCond is null, subModuleID:%v", subModule.ID.Hex()))
return
}
data.HasNext = next
data.AllVideoInfo = vidhelpser.NewEncodeVideoInfoNotStatus(videos)
// tagId 不为空,找出video的tag, 只返回一个,前端统一
if !tagId.IsZero() {
var tag vidmod.TagInfo
for _, video := range data.AllVideoInfo {
if tag.ID.IsZero() {
for _, t := range video.Tags {
if t.ID == tagId {
tag = t
}
}
}
video.Tags = append(video.Tags[0:0], tag)
}
}
return
}
func HandleCommunityPICAndGame(subModule moduleconfmod.ModuleConf, req VideoUnderSubModuleReq) (data VideoUnderSubModuleResp) {
var (
err error
videos []*vidmod.VideoModel
next bool
)
var (
filter = bson.M{}
sort = bson.D{}
someTimeAgo = time.Now().AddDate(-3, 0, 0) // 给个默认时间统一走索引
opt = options.Find().SetSkip(req.Skip64()).SetLimit(req.Limit64()).SetProjection(bson.M{"richText": 0})
)
// 黄油 (本项目没用到)
//最热:根据点赞量排序 sortType = 2
//最新:根据上架时间排序 sortType = 1
//畅销:根据解锁次数排序 sortType = 8
//图集
//最多观看:观看量最多 sortType = 3
//最新发布:根据上架时间排序 sortType = 1
//最多收藏:根据收藏量排序 sortType = 7
filter = bson.M{"mId": subModule.ID.Hex(), "status": vidmod.CheckPass}
switch req.ModuleSort {
case 2: // 精选/最多喜欢 // x天内点赞最多的
filter["reviewAt"] = bson.M{"$gte": someTimeAgo}
sort = bson.D{{"liaoBaTopSort", -1}, {"likeCount", -1}, {"reviewAt", -1}}
case 3: // 最热/最多播放 x天内观看量
filter["reviewAt"] = bson.M{"$gte": someTimeAgo}
sort = bson.D{{"playCount", -1}, {"reviewAt", -1}}
case 7: // 推荐/最多收藏 x天内收藏最多
filter["reviewAt"] = bson.M{"$gte": someTimeAgo}
sort = bson.D{{"collectCount", -1}, {"reviewAt", -1}}
case 8: // 购买人数
filter["reviewAt"] = bson.M{"$gte": someTimeAgo}
sort = bson.D{{"purchaseCount", -1}, {"reviewAt", -1}}
case 1: // 最新
fallthrough
default:
sort = bson.D{{"reviewAt", -1}}
}
videos, next, err = vidmod.FindList(filter, opt.SetSort(sort)) // 获取视频详情
if err != nil {
log.Error(fmt.Sprintf("vidmod.GetVideoListByCond err:%v", err))
return
}
if len(videos) == 0 {
log.Warn(fmt.Sprintf("vidmod.GetVideoListByCond is null, subModuleID:%v", subModule.ID.Hex()))
return
}
data.HasNext = next
data.AllVideoInfo = vidhelpser.NewEncodeVideoInfoNotStatus(videos)
return
}
// GetCommunityRecommend 社区-图集和黄油推荐
func GetCommunityRecommend(req CommunityRecommendReq) (data *VideoUnderSubModuleResp, err error) {
ret := VideoUnderSubModuleResp{
HasNext: false,
AllVideoInfo: []*vidmod.VideoInfoResp{},
AllSection: []Section{},
}
excluded, err := moduleconfmod.ExcludedVideoModuleIDs(time.Now(), true)
if err != nil {
return &ret, err
}
if len(excluded) > 0 {
result := HandleHotRecommend(req)
return &result, nil
}
expire := time.Minute * 5
_, err = cachev2.Classes().CacheTime(expire).AutoListKey("CommunityRecommend").ResBind(&ret).Cache(HandleHotRecommend, req)
if err != nil {
return
}
return &ret, nil
}
// HandleHotRecommend 社区热门推荐
func HandleHotRecommend(req CommunityRecommendReq) (resp VideoUnderSubModuleResp) {
var (
sort bson.D
someTimeAgo = time.Now().AddDate(0, -3, 0)
filter = bson.M{"newsType": req.NewsType, "status": vidmod.CheckPass, "reviewAt": bson.M{"$gte": someTimeAgo}}
opt = options.Find().SetSkip(int64(req.Skip())).SetLimit(int64(req.Limit())).SetProjection(bson.M{"richText": 0})
)
excluded, err := moduleconfmod.ExcludedVideoModuleIDs(time.Now(), true)
if err != nil {
return
}
if len(excluded) > 0 {
filter["mId"] = bson.M{"$nin": excluded}
}
// 黄油 90天内的帖子
//最热:根据点赞量排序 sortType = 2
//最新:根据上架时间排序 sortType = 1
//畅销:根据解锁次数排序 sortType = 8
//图集 90天内的帖子
//最多观看:观看量最多 sortType = 3
//最新发布:根据上架时间排序 sortType = 1
//最多收藏:根据收藏量排序 sortType = 7
switch req.SortType {
case 2: // 精选/最多喜欢 // x天内点赞最多的
filter["reviewAt"] = bson.M{"$gte": someTimeAgo}
sort = bson.D{{"likeCount", -1}, {"reviewAt", -1}}
case 3: // 最热/最多播放 x天内观看量
filter["reviewAt"] = bson.M{"$gte": someTimeAgo}
sort = bson.D{{"playCount", -1}, {"reviewAt", -1}}
case 7: // 推荐/最多收藏 x天内收藏最多
filter["reviewAt"] = bson.M{"$gte": someTimeAgo}
sort = bson.D{{"liaoBaTopSort", -1}, {"collectCount", -1}, {"reviewAt", -1}}
case 8: // 购买人数
filter["reviewAt"] = bson.M{"$gte": someTimeAgo}
sort = bson.D{{"purchaseCount", -1}, {"reviewAt", -1}}
case 1: // 最新
fallthrough
default:
sort = bson.D{{"reviewAt", -1}}
}
videos, next, err := vidmod.FindList(filter, opt.SetSort(sort)) // 获取视频详情
if err != nil {
log.Error(fmt.Sprintf("vidmod.GetVideoListByCond err:%v", err))
return
}
if len(videos) == 0 {
return
}
resp.HasNext = next
resp.AllVideoInfo = vidhelpser.NewEncodeVideoInfoNotStatusForRecommend(videos)
return resp
}
// IncSectionHits 增加专题点击量
func IncSectionHits(ctx context.Context, sectionID primitive.ObjectID) error {
now := time.Now()
date := now.Add(-12 * time.Hour).Local().Format("2006-01-02")
return sectionstatmod.UpsertSectionHits(sectionID, date)
}
// GetBloggerVideos 获取某一原创博主的视频列表
func GetBloggerVideos(uid, bloggerID uint64, page commod.Page) (resp VideoUnderSectionResp, err error) {
videos, hasNext, err := vidmod.GetBloggerVideos(bloggerID, int64(page.Skip()), int64(page.Limit()))
if err != nil {
return
}
resp.HasNext = hasNext
bloggerInfo, err := getOriginalBloggerInfo([]uint64{bloggerID})
if err != nil {
return
}
resp.OriginalBloggerInfo = bloggerInfo[bloggerID]
resp.Videos = vidhelpser.NewEncodeVideoInfo(uid, videos)
sort.Slice(resp.Videos, func(i, j int) bool {
return resp.Videos[i].CreatedAt.After(resp.Videos[j].CreatedAt)
})
return
}
func GetSectionVideos(uid uint64, sectionID primitive.ObjectID, page commod.Page, sortType string, playTimeType int) (
out VideoUnderSectionResp, err error,
) {
var sectionVideos []modulevidmod.SectionVideo
defer func() {
vidhelpser.ApplyFreeTrialBadgeToVideoInfoResps(vidhelpser.LoadFreeTrialBadgeContext(uid), out.Videos)
}()
section, err := modulesectionmod.GetBySectionByID(sectionID)
if err != nil {
return out, err
}
if section.ID.IsZero() {
return out, errors.New("section not found")
}
active, err := moduleconfmod.CanBrowseModule(section.SubModuleID, time.Now())
if err != nil {
return out, err
}
if !active {
return out, nil
}
key := redisconst.SectionVideoInfoCacheKey(sectionID.Hex(), sortType, int64(page.PageSize), int64(page.PageNumber))
str, err := appg.Redis.Get(key)
if err != nil {
log.Warn(fmt.Sprintf("用户ID:%d;缓存获取视频列表信息异常:%v", uid, err))
}
if str != nil {
if err = msgpack.Unmarshal([]byte(*str), &out); err == nil {
return out, nil
}
log.Warn(fmt.Sprintf("用户ID:%d;解析缓存数据异常:%v", uid, err))
}
limit100 := false
skip := page.Skip()
if strings.TrimSpace(section.SectionName) == "最新" { // "最新"栏最多返回100条
limit100 = true
if skip >= 100 {
return out, nil
}
}
// 默认专题排序
if sortType == "" && playTimeType == 0 {
opts := options.Find().SetLimit(int64(page.PageSize + 1)).SetSkip(int64((page.PageNumber - 1) * page.PageSize)).
SetSort(bson.D{{Key: "sortCode", Value: -1}, {Key: "videoReviewedAt", Value: -1}})
sectionVideos, err = modulevidmod.Find(bson.M{"sectionID": sectionID}, opts)
if err != nil {
log.Error(fmt.Sprintf("用户ID:%d;专题ID:%v;获取专题视频异常:%v", uid, sectionID, err))
return out, err
}
out.HasNext = false
if uint64(len(sectionVideos)) > page.PageSize {
sectionVideos = sectionVideos[:page.PageSize]
out.HasNext = true
}
} else {
sectionVideos, err = modulevidmod.GetSectionVideosBySectionID(sectionID)
if err != nil {
return out, err
}
}
videoIDs := make([]primitive.ObjectID, len(sectionVideos))
for i, v := range sectionVideos {
videoIDs[i] = v.VideoID
}
if len(videoIDs) == 0 {
out.Videos = []*vidmod.VideoInfoResp{}
return out, nil
}
cond := bson.M{"_id": bson.M{"$in": videoIDs}, "status": 1}
/* 19porn used sort
热门推荐:一个月内视频按点赞量排序 支持运营手动设置推荐
最新上架:根据发布的时间顺序进行排序,无限下滑
最新热评:按照用户评论排序。优先级在置顶排序后边,按天刷新
*/
var s bson.D
switch sortType {
case "watch":
s = bson.D{{Key: "playCount", Value: -1}, {Key: "reviewAt", Value: -1}, {Key: "_id", Value: -1}}
case "new", "":
s = bson.D{{Key: "reviewAt", Value: -1}, {Key: "_id", Value: -1}}
case "collect":
s = bson.D{{Key: "collectCount", Value: -1}, {"reviewAt", -1}, {Key: "_id", Value: -1}}
case "like":
s = bson.D{{Key: "likeCount", Value: -1}, {"reviewAt", -1}, {Key: "_id", Value: -1}}
case "comment": // hot comment, sort value , from task timed statistics of 'SKD' server got it
s = bson.D{{Key: "hot", Value: -1}, {"reviewAt", -1}, {Key: "_id", Value: -1}}
default: //兼容旧版,传入参数为其他值
s = bson.D{{"reviewAt", -1}, {Key: "_id", Value: -1}}
}
videos, total, err := vidmod.GetvideoListByMapCond(cond, s, page.PageNumber, page.PageSize)
if err != nil {
return out, err
}
var newVideos []*vidmod.VideoModel
if playTimeType == 0 && sortType == "" {
newVideos = make([]*vidmod.VideoModel, 0, len(sectionVideos))
for _, sv := range sectionVideos {
for _, v := range videos {
if sv.VideoID == v.ID {
newVideos = append(newVideos, v)
break
}
}
}
} else {
newVideos = videos
}
if int64(page.PageNumber*page.PageSize) < total {
out.HasNext = true
}
if newVideosLen := uint64(len(newVideos)); limit100 && skip+newVideosLen >= 100 {
out.HasNext = false
newVideos = newVideos[:100-skip]
}
out.Videos = vidhelpser.NewEncodeVideoInfo(uid, newVideos)
common.Go(func() {
if out.Videos != nil || len(out.Videos) > 0 {
d, err := msgpack.Marshal(out)
if err != nil {
return
}
if err = appg.Redis.Set(key, d, 10*time.Minute); err != nil {
log.Warn(fmt.Sprintf("用户ID:%d;保存缓存数据异常:%v", uid, err))
}
}
})
return out, nil
}
func GetVideosUnderSection(uid uint64, sectionID primitive.ObjectID, page commod.Page, sortType string, playTimeType int) (
resp VideoUnderSectionResp, err error) {
section, err := modulesectionmod.GetBySectionByID(sectionID)
if err != nil {
return resp, err
}
active, err := moduleconfmod.CanBrowseModule(section.SubModuleID, time.Now())
if err != nil || !active {
return resp, err
}
sectionVideos, err := modulevidmod.GetSectionVideosBySectionID(sectionID)
if err != nil {
return
}
videoIDs := make([]primitive.ObjectID, len(sectionVideos))
for i, v := range sectionVideos {
videoIDs[i] = v.VideoID
}
cond := bson.M{"status": 1, "_id": bson.M{"$in": videoIDs}}
switch playTimeType {
case 1:
cond["playTime"] = bson.M{"$gte": 60 * 10}
case 2:
cond["playTime"] = bson.M{"$lt": 60 * 10}
}
var s bson.D
switch sortType {
case "hot":
videos, _, hasNext, err := vidmod.GetHotList(int64((page.PageNumber-1)*page.PageSize), int64(page.PageSize), cond)
vInfos := vidhelpser.NewEncodeVideoInfo(uid, videos)
resp.Videos = vInfos
resp.HasNext = hasNext
return resp, err
case "watch":
s = bson.D{{Key: "playCount", Value: -1}, {Key: "reviewAt", Value: -1}}
case "like":
s = bson.D{{Key: "likeCount", Value: -1}, {Key: "reviewAt", Value: -1}}
case "new", "":
s = bson.D{{Key: "reviewAt", Value: -1}}
default: //兼容旧版,传入参数为其他值
s = bson.D{{Key: "playCount", Value: -1}, {Key: "reviewAt", Value: -1}}
}
videos, total, err := vidmod.GetvideoListByMapCond(cond, s, page.PageNumber, page.PageSize)
if err != nil {
return resp, err
}
videoInfo := vidhelpser.NewEncodeVideoInfo(uid, videos)
resp.Videos = videoInfo
if int64(page.PageNumber*page.PageSize) < total {
resp.HasNext = true
}
resp.OriginalBloggerInfo = OriginalBloggerInfo{}
return
}
type HotVideoRes struct {
List []*vidmod.VideoInfo `json:"data"`
HasNext bool `json:"hasNext"`
}
// GetHotVideo 获取最热视频
func GetHotVideo(page, size int64, t int) (res HotVideoRes, err error) {
key := fmt.Sprintf("hot-videos:page%d:size%d:t%d", page, size, t)
expire := time.Minute * 10
s, err := appg.Redis.Get(key)
if err != nil {
return
}
if s != nil && *s != "" {
_ = json.Unmarshal([]byte(*s), &res)
}
if len(res.List) == 0 {
start := time.Now().AddDate(0, 0, -7)
end := time.Now()
switch t {
case 1: // 本月最热
start = time.Now().AddDate(0, -1, 0)
case 2: // 上月最热
start = time.Now().AddDate(0, -2, 0)
end = time.Now().AddDate(0, -1, 0)
}
var videos []*vidmod.VideoModel
videos, res.HasNext, err = vidmod.GetHotVideoByOnlineTimeRange(start, end, page, size)
if err != nil {
return HotVideoRes{}, err
}
res.List = vidhelpser.EncodeVideoInfoNoUID(videos)
common.Go(func() {
b, _ := json.Marshal(res)
_ = appg.Redis.Set(key, string(b), expire)
})
}
return
}
func GetHotPublisher(uid uint64) (data []*proto.HotPublisher, err error) {
data = make([]*proto.HotPublisher, 0)
return
key := "getHotpublisher:vidser"
expaire := time.Minute * 10
s, err := appg.Redis.Get(key)
if err != nil {
return
}
if s != nil && *s != "" {
_ = json.Unmarshal([]byte(*s), &data)
}
if len(data) == 0 {
now := time.Now()
d := timeutil.BeginningOfDay(now)
reidsKey := redisconst.PlayLeaderboardKey("publiser", d.Unix())
var publiserSli []string
publiserSli, err = appg.Redis.ZRange(reidsKey, 0, 20)
if err != nil {
return
}
//数据量少用头一天的
if len(publiserSli) < 20 {
reidsKey = redisconst.PlayLeaderboardKey("publiser", d.Add(-time.Hour*24).Unix())
publiserSli, err = appg.Redis.ZRange(reidsKey, 0, 20)
if err != nil {
return
}
}
publiserUIDs := make([]uint64, len(publiserSli))
for i := range publiserSli {
publiserUIDs[i], err = strconv.ParseUint(publiserSli[i], 10, 64)
if err != nil {
publiserUIDs[i] = 0
}
}
data = make([]*proto.HotPublisher, 0, len(publiserUIDs))
for _, v := range publiserUIDs {
if v == 0 {
continue
}
hotPublisherVid := proto.HotPublisher{
BaseInfo: usermod.BaseInfo{},
List: make([]*vidmod.VideoInfo, 0),
}
var u *usermod.User
u, err = usermod.FindUserByUID(v)
if err != nil || u == nil {
return
}
hotPublisherVid.BaseInfo = usermod.BaseInfo{
UID: u.UID,
Name: u.Name,
Gender: u.Gender,
Portrait: u.Portrait,
HasLocked: u.HasLocked,
HasBanned: u.HasBanned,
}
var vidMods []*vidmod.VideoModel
vidMods, _, _, err = vidmod.GetVideoListByPublishers([]uint64{v}, 1, 4)
if err != nil || len(vidMods) <= 0 {
return
}
hotPublisherVid.List = vidhelpser.EncodeVideoInfo(uid, vidMods)
data = append(data, &hotPublisherVid)
}
common.Go(func() {
b, _ := json.Marshal(data)
_ = appg.Redis.Set(key, string(b), expaire)
})
}
return
}
func getOriginalBloggerInfo(uids []uint64) (info map[uint64]OriginalBloggerInfo, err error) {
users, err := usermod.FindUsersByUID(uids)
if err != nil {
return
}
info = make(map[uint64]OriginalBloggerInfo)
for _, user := range users {
info[user.UID] = OriginalBloggerInfo{
UID: user.UID,
Name: user.Name,
Portrait: user.Portrait,
OfficialCert: user.OfficialCert,
Summary: user.Summary,
}
}
return
}
// List 新版视频列表
func List(uid uint64, req *vidmod.AppListReq) (interface{}, stderr.Code) {
var (
head []*vidmod.VideoModel
err error
)
data, _ := json.Marshal(req)
log.Info(fmt.Sprintf("新版视频列表请求参数:%s", string(data)))
filter, err := req.Filter(uid)
if err != nil {
log.Error(fmt.Sprintf("获取视频列表 发生错误:%v", err), log.Any("uid", uid), log.Any("params", req))
return nil, stderr.ErrParamError
}
log.Info("---", log.Any("filter", filter))
hasNext := false
if req.Type == vidmod.Image && req.Model == vidmod.HappinessPlaza {
head, err = getImageTopList(req.FilterType, filter, req.Options())
if err != nil {
log.Error(fmt.Sprintf("获取幸福广场列表 发生错误:%v", err), log.Any("uid", uid), log.Any("params", req))
return nil, stderr.ErrDbQueryError
}
startIndex, endIndex := common.SlicePage(req.PageNumber, req.PageSize, uint64(len(head)))
head = head[startIndex:endIndex]
if len(head) >= int(req.PageSize) {
hasNext = true
}
newInfos := vidhelpser.EncodeVideoInfo(uid, head)
return vidmod.NewsListResp{List: newInfos, HasNext: hasNext}, stderr.Success
}
// 首先从redis缓存内获取相关信息
redisKey := req.RedisKey(uid)
if redisKey != "" {
ct, err := appg.Redis.Get(redisKey)
if err != nil && ct != nil && *ct != "" {
if err = json.Unmarshal([]byte(*ct), &head); err == nil {
if len(head) > int(req.PageSize) {
hasNext = true
head = head[:req.PageSize]
}
newInfos := vidhelpser.EncodeVideoInfo(uid, head)
return vidmod.NewsListResp{List: newInfos, HasNext: hasNext}, stderr.Success
}
}
}
list, err := vidmod.GetList(filter, req.Options())
if err != nil {
log.Error(fmt.Sprintf("获取视频列表 发生错误:%v", err), log.Any("uid", uid), log.Any("params", req))
return nil, stderr.ErrDbQueryError
}
// 设置/刷新redis缓存
if redisKey != "" {
go func() {
bts, err := json.Marshal(list)
if err != nil {
return
}
_ = appg.Redis.Set(redisKey, string(bts), time.Minute*3)
}()
}
head = list
if len(list) > int(req.PageSize) {
hasNext = true
head = list[:req.PageSize]
}
newInfos := vidhelpser.EncodeVideoInfo(uid, head)
return vidmod.NewsListResp{List: newInfos, HasNext: hasNext}, stderr.Success
}
func AllVideosOfModule(subModuleID primitive.ObjectID, uid uint64, req AllVideosOfModuleReq) (AllVideosOfModuleResp, error) {
ret := AllVideosOfModuleResp{
HasNext: false,
Videos: make([]*vidmod.VideoInfo, 0),
}
active, err := moduleconfmod.CanBrowseModule(subModuleID, time.Now())
if err != nil || !active {
return ret, err
}
sections, err := modulesectionmod.GetAllBySubModuleID(subModuleID)
if err != nil {
return ret, err
}
sectionIds := make([]primitive.ObjectID, len(sections))
for i, v := range sections {
sectionIds[i] = v.ID
}
sectionVideos, err := modulevidmod.GetVideosBySectionIDs(sectionIds)
if err != nil {
return ret, err
}
vids := make([]primitive.ObjectID, len(sectionVideos))
for i, v := range sectionVideos {
vids[i] = v.VideoID
}
cond := bson.M{"status": 1, "_id": bson.M{"$in": vids}}
switch req.PlayTimeType {
case 1:
cond["playTime"] = bson.M{"$gte": 60 * 10}
case 2:
cond["playTime"] = bson.M{"$lt": 60 * 10}
}
var sort bson.D
switch req.SortType {
case "hot":
videos, _, hasNext, err := vidmod.GetHotList(int64((req.PageNumber-1)*req.PageSize), int64(req.PageSize), cond)
ret.Videos = vidhelpser.EncodeVideoInfo(uid, videos)
ret.HasNext = hasNext
return ret, err
case "watch":
sort = bson.D{{Key: "playCount", Value: -1}, {Key: "reviewAt", Value: -1}}
case "like":
sort = bson.D{{Key: "likeCount", Value: -1}, {Key: "reviewAt", Value: -1}}
case "new", "":
sort = bson.D{{Key: "reviewAt", Value: -1}}
default: //兼容旧版,传入参数为其他值
sort = bson.D{{Key: "playCount", Value: -1}, {Key: "reviewAt", Value: -1}}
}
videos, total, err := vidmod.GetvideoListByMapCond(cond, sort, req.PageNumber, req.PageSize)
if err != nil {
return ret, err
}
vInfos := vidhelpser.EncodeVideoInfo(uid, videos)
ret.Videos = vInfos
if int64(req.PageNumber*req.PageSize) < total {
ret.HasNext = true
}
return ret, nil
}
// 获取幸福广场置顶帖子
func getImageTopList(topType uint, filter1 bson.M, opts *options.FindOptions) ([]*vidmod.VideoModel, error) {
key := fmt.Sprintf(redisconst.ImageTopKey, topType)
//读取缓存
s, err := appg.Redis.Get(key)
if err != nil {
return nil, err
}
if s != nil && *s != "" {
vidModels := make([]*vidmod.VideoModel, 0)
if err = json.Unmarshal([]byte(*s), &vidModels); err != nil {
return nil, err
}
return vidModels, nil
}
filter := bson.M{}
var opt *options.FindOptions
switch topType {
case 1:
filter["recommendEndTime"] = bson.M{"$gt": time.Now()}
opt = options.Find().SetSort(bson.D{{Key: "recommendSort", Value: -1}})
case 2:
filter["newEndTime"] = bson.M{"$gt": time.Now()}
opt = options.Find().SetSort(bson.D{{Key: "createdAt", Value: -1}})
default:
filter["newEndTime"] = bson.M{"$gt": time.Now()}
opt = options.Find().SetSort(bson.D{{Key: "newSort", Value: -1}})
}
imageTops, err := imagetopmod.GetImageTopList(filter, opt)
if err != nil {
log.Error(fmt.Sprintf("获取置顶帖子列表 发生错误:%v", err), log.Any("params", topType))
return nil, err
}
//查询所有的幸福社区的帖子
filter1["happinessPlazaTop"] = bson.M{"$ne": 1}
list, _ := vidmod.GetList(filter1, opts)
//根据置顶帖子查询过滤出来视频id
imageTopsLen := len(imageTops)
vidModels := make([]*vidmod.VideoModel, imageTopsLen, imageTopsLen+len(list))
for i, info := range imageTops {
videoInfo, err := vidmod.GetVideoByID(info.ImageId)
if err != nil {
log.Error(fmt.Sprintf("获取置顶帖子列表 发生错误:%v", err), log.Any("params", topType))
return nil, err
}
vidModels[i] = videoInfo
}
vidModels = append(vidModels, list...)
//设置缓存
common.Go(func() {
b, _ := json.Marshal(vidModels)
_ = appg.Redis.Set(key, string(b), redisconst.ImageTopExpire)
})
return vidModels, nil
}
func GetValidSections() ([]SectionResp, error) {
sections, err := modulesectionmod.AllSections()
if err != nil {
return nil, err
}
sectionsLen := len(sections)
if sectionsLen == 0 {
return nil, nil
}
sort.Slice(sections, func(i, j int) bool {
if sections[i].Sort == nil {
return true
}
if sections[j].Sort == nil {
return false
}
return *sections[i].Sort < *sections[j].Sort
})
srs := make([]SectionResp, sectionsLen)
for i := range sections {
srs[i].ID = sections[i].ID
srs[i].SectionName = sections[i].SectionName
}
return srs, nil
}
func GetVidPlayRecommand(uid uint64, vid primitive.ObjectID) (*RecommandResp, error) {
svs, err := modulevidmod.GetSectionsByVideoId(vid)
if err != nil {
return nil, err
}
svsLen := len(svs)
if svsLen == 0 {
return GetSearchRecommand(uid)
}
sectionIDs := make([]primitive.ObjectID, svsLen)
for i, sv := range svs {
sectionIDs[i] = sv.SectionID
}
svs, err = modulevidmod.GetVideosBySectionIDs(sectionIDs)
if err != nil {
return nil, err
}
svsLen = len(svs)
if svsLen == 0 {
return GetSearchRecommand(uid)
}
vids := make([]primitive.ObjectID, 0, svsLen)
for _, sv := range svs {
if sv.VideoID == vid || sv.VideoID.IsZero() {
continue
}
vids = append(vids, sv.VideoID)
}
if len(vids) == 0 {
return GetSearchRecommand(uid)
}
videos, err := vidmod.GetVideosRecommandInVids(vids)
if err != nil {
return nil, err
}
if len(videos) == 0 {
return GetSearchRecommand(uid)
}
return &RecommandResp{
Videos: vidhelpser.EncodeVideoInfoForRecommend(uid, videos),
HasNext: false,
}, nil
}
func GetVidPlayRecommand_new(uid uint64, vid primitive.ObjectID) (data *RecommandResp, err error) {
excludedModules, err := moduleconfmod.ExcludedVideoModuleIDs(time.Now(), true)
if err != nil {
return nil, err
}
useCache := len(excludedModules) == 0
svs, err := modulevidmod.GetSectionsByVideoId(vid)
if err != nil {
return nil, err
}
svsLen := len(svs)
if svsLen == 0 {
return GetSearchRecommand(uid)
}
sectionIDs := make([]primitive.ObjectID, svsLen)
for i, sv := range svs {
sectionIDs[i] = sv.SectionID
}
//加一个搜索缓存
var key string
if len(sectionIDs) > 0 {
key = fmt.Sprintf("GetVidPlayRecommand_new%v", sectionIDs[0])
}
expaire := time.Minute * 5
if useCache {
s, cacheErr := appg.Redis.Get(key)
if cacheErr != nil {
return nil, cacheErr
}
if s != nil && *s != "" {
if cacheErr = json.Unmarshal([]byte(*s), &data); cacheErr != nil {
return nil, cacheErr
}
return data, nil
}
}
fmt.Println("--------------------1")
var videos []*vidmod.VideoModel
if data == nil {
svs, err = modulevidmod.GetVideosBySectionIDs(sectionIDs)
if err != nil {
return nil, err
}
svsLen = len(svs)
if svsLen == 0 {
return GetSearchRecommand(uid)
}
vids := make([]primitive.ObjectID, 0, svsLen)
fmt.Println("--------------------2")
for _, sv := range svs {
if sv.VideoID == vid || sv.VideoID.IsZero() {
continue
}
vids = append(vids, sv.VideoID)
}
if len(vids) == 0 {
return GetSearchRecommand(uid)
}
fmt.Println("----------------------------vids", vids)
videos, err = vidmod.GetVideosRecommandInVids(vids)
if err != nil {
return nil, err
}
if len(videos) == 0 {
return GetSearchRecommand(uid)
}
fmt.Println("--------------------3")
data = &RecommandResp{Videos: vidhelpser.EncodeVideoInfoForRecommend(uid, videos), HasNext: false}
if useCache {
common.Go(func() {
b, _ := json.Marshal(data)
_ = appg.Redis.Set(key, string(b), expaire)
})
}
}
return data, nil
}
func GetSearchRecommand(uid uint64) (*RecommandResp, error) {
videos, err := vidmod.GetSearchRecommand()
if err != nil {
return nil, err
}
return &RecommandResp{
Videos: vidhelpser.EncodeVideoInfoForRecommend(uid, videos),
HasNext: false,
}, nil
}
// GetVideosByTagID 通过标签id获取视频列表信息
func GetVideosByTagID(tid primitive.ObjectID, newsType string, sort int, skip, limit uint64) (list []vidmod.RecoVideoModel, err error) {
redisKey := redisconst.GetTagSortVideoKey(tid.Hex(), newsType, sort, skip, limit)
if !appg.Redis.Exists(redisKey) {
list, err = vidmod.VideoListByTagIDSort(tid, newsType, sort, skip, limit)
if err != nil {
return
}
go func() {
b, _ := json.Marshal(list)
_ = appg.Redis.Set(redisKey, string(b), redisconst.GetTagSortVideoExpire())
}()
return
}
strP, err := appg.Redis.Get(redisKey)
if err != nil {
return
}
if strP != nil {
err = json.Unmarshal([]byte(*strP), &list)
} else {
err = errors.New("redis GetVideosByTagID get nil")
}
return
}
func GetRecommendList(uid uint64, tagId string, newsType string, page commod.Page) (list []*vidmod.VideoInfo, hasNext bool, err error) {
tagObjId, _ := primitive.ObjectIDFromHex(tagId)
filter := bson.M{"status": 1, "newsType": newsType}
if !tagObjId.IsZero() {
filter["tags"] = tagObjId
}
res, hasNext, err := viddata.GetListFromCache(filter, int64(page.Skip()), int64(page.Limit()),
bson.D{{Key: "likeCount", Value: -1}, {Key: "reviewAt", Value: -1}})
if err != nil {
return
}
list = vidhelpser.EncodeVideoInfoForRecommend(uid, res)
return list, hasNext, nil
}
// -------- 查询亚模块下的所有视屏列表 ---------
type SubModuleAllVideoListReq struct {
commod.Page
Type int `form:"type"` // 0 最多观看 1 最新上架 3 最多收藏
}
func SubModuleAllVideoList(uid uint64, subModuleId primitive.ObjectID, req SubModuleAllVideoListReq) (resp VideoUnderSubModuleResp, err error) {
active, err := moduleconfmod.CanBrowseModule(subModuleId, time.Now())
if err != nil || !active {
return resp, err
}
expire := time.Minute * 10
var videos []*vidmod.VideoModel
_, err = cachev2.Classes().CacheTime(expire).AutoListKey("HomePage:MaybeYouLike").ResBind(&videos).Cache(findAllVideoByModuleId, subModuleId, req)
if err != nil || len(videos) == 0 {
return resp, err
}
if uint64(len(videos)) > req.PageSize {
resp.HasNext = true
videos = videos[:req.PageSize]
}
vInfos := vidhelpser.NewEncodeVideoInfoNotStatusForUser(uid, videos)
resp.AllVideoInfo = vInfos
return resp, nil
}
func findAllVideoByModuleId(subModuleId primitive.ObjectID, req SubModuleAllVideoListReq) []*vidmod.VideoModel {
subModule, err := moduleconfmod.GetByID(subModuleId)
if err != nil {
return nil
}
if !subModule.IsActiveAt(time.Now()) {
return nil
}
var (
skip = int64((req.PageNumber - 1) * req.PageSize)
sort bson.D
)
switch req.Type {
case 1:
sort = bson.D{{"reviewAt", -1}, {"_id", -1}}
case 3:
sort = bson.D{{"collectCount", -1}, {"reviewAt", -1}, {"_id", -1}}
default: // 0
sort = bson.D{{"playCount", -1}, {"reviewAt", -1}, {"_id", -1}}
}
filter := bson.M{"mId": subModule.ID.Hex(), "status": 1}
videos, err := vidmod.GetList(filter, options.Find().SetSkip(skip).SetLimit(int64(req.PageSize+1)).SetSort(sort))
if err != nil {
log.Error("findAllVideoByModuleId vidmod.GetVideosByIDs", log.E(err))
return nil
}
return videos
}
type (
HandleShortVideoUnderSubModuleReq struct {
commod.Page
UID uint64 `json:"-" form:"-"`
Context context.Context `json:"-" form:"-"`
RequestID string `json:"-" form:"-"`
Err error `json:"-" form:"-"`
}
HandleShortVideoUnderSubModuleRep VideoUnderSubModuleResp
)
func (req *HandleShortVideoUnderSubModuleReq) Do() (data VideoUnderSubModuleResp) {
if req.UID > 0 {
return req.get()
}
excluded, err := moduleconfmod.ExcludedVideoModuleIDs(time.Now(), true)
if err != nil || len(excluded) > 0 {
return req.get()
}
cachev2.Classes().CacheTime(time.Minute * 5).AutoListKey("ShortVideoRecoList").ResBind(&data).Cache(req.get)
return data
}
func (req *HandleShortVideoUnderSubModuleReq) get() (data VideoUnderSubModuleResp) {
if req.UID > 0 && appg.Conf.ShortRecommendEnabled() {
ctx := req.Context
if ctx == nil {
ctx = context.Background()
}
fetchCtx, cancel := context.WithTimeout(ctx, appg.Conf.ShortRecommendRequestTimeout())
result, fetchErr := shortrecommendser.FetchScoped(
fetchCtx, req.UID, int(req.PageSize), req.RequestID, "module-short-all",
)
cancel()
if result.QueueVersion != "" {
if fetchErr != nil && len(result.Videos) == 0 {
log.Error("short module recommend fetch failed after reservation",
log.E(fetchErr),
log.Any("uid", req.UID),
log.Any("queueVersion", result.QueueVersion),
log.Any("queueLength", result.QueueLength),
log.Any("scanned", result.Scanned),
log.Any("batches", result.Batches))
req.Err = fetchErr
return data
}
if fetchErr != nil || result.BudgetExceeded || result.Filtered > 0 {
log.Warn("short module recommend fetch completed with filtering",
log.E(fetchErr),
log.Any("uid", req.UID),
log.Any("queueVersion", result.QueueVersion),
log.Any("queueLength", result.QueueLength),
log.Any("requested", req.PageSize),
log.Any("returned", len(result.Videos)),
log.Any("scanned", result.Scanned),
log.Any("filtered", result.Filtered),
log.Any("batches", result.Batches),
log.Any("budgetExceeded", result.BudgetExceeded))
}
data.AllVideoInfo = vidhelpser.NewEncodeVideoInfoNotStatusForUser(req.UID, result.Videos)
data.HasNext = result.QueueLength > 0
return data
}
if fetchErr != nil {
if errors.Is(fetchErr, context.Canceled) ||
errors.Is(fetchErr, context.DeadlineExceeded) {
return data
}
log.Error("short module recommend fetch failed, fallback random set",
log.E(fetchErr), log.Any("uid", req.UID))
}
}
excluded, err := moduleconfmod.ExcludedVideoModuleIDs(time.Now(), true)
if err != nil {
return data
}
excludedSet := make(map[string]struct{}, len(excluded))
for _, moduleID := range excluded {
excludedSet[moduleID] = struct{}{}
}
totalSize, err := appg.Redis.SCard(redisconst.ShortVideosRecoCacheKey)
if err != nil {
log.Error("GetVidList SCard err", log.E(err))
return data
}
if totalSize == 0 {
return data
}
if totalSize <= int64(req.PageSize) {
data.HasNext = false
} else {
data.HasNext = math.Ceil(float64(totalSize)/float64(req.PageSize)) > float64(req.PageNumber)
}
sampleSize := int64(req.PageSize * 5)
if sampleSize > totalSize {
sampleSize = totalSize
}
pushVid, err := redis.Handler.SRandMemberN(redisconst.ShortVideosRecoCacheKey, sampleSize)
if err != nil {
return data
}
vids := common.String2ObjectID(pushVid)
vList, err := vidmod.GetVideosByIDs(vids)
if err != nil {
return data
}
allowed := make([]*vidmod.VideoModel, 0, req.PageSize)
for _, video := range vList {
if video == nil {
continue
}
if _, blocked := excludedSet[video.MID]; blocked {
continue
}
allowed = append(allowed, video)
if uint64(len(allowed)) == req.PageSize {
break
}
}
data.AllVideoInfo = vidhelpser.NewEncodeVideoInfoNotStatusForUser(req.UID, allowed)
return data
}
// GetChangeVideos 获取换一批的视频列表
func GetChangeVideos(in *vidmod.ChangeVideoSectionReq, uid uint64) (resp VideoUnderSectionResp, err error) {
sectionID, err := primitive.ObjectIDFromHex(in.SectionID)
if err != nil {
log.Error(fmt.Sprintf("GetChangeVideos primitive objectIDFromHex err:%v", err))
return
}
// 获专题信息
section, err := modulesectionmod.GetBySectionByID(sectionID)
if err != nil {
log.Error(fmt.Sprintf("GetChangeVideos modulesectionmod getBySectionByID err:%v", err))
return
}
if section.ID.IsZero() {
return
}
active, err := moduleconfmod.CanBrowseModule(section.SubModuleID, time.Now())
if err != nil || !active {
return resp, err
}
// 获取专题下视频信息
filter := bson.M{"sectionID": sectionID}
op := options.Find().SetSort(bson.M{"createdAt": -1}).SetLimit(300)
sectionVideos, err := modulevidmod.Find(filter, op)
if err != nil {
return
}
if len(sectionVideos) < 0 {
return
}
var vIds []primitive.ObjectID
for _, v := range sectionVideos {
vIds = append(vIds, v.VideoID)
}
// 根据样式判断随机数据
num := modulesectionmod.ShowTypeHashVale[section.ShowType]
if len(vIds) < num {
return
}
if num == 0 {
num = 9
}
rp := common.NewRandomPicker()
pickElements, err := rp.PickElements(vIds, num)
if err != nil {
return
}
objectIDs := make([]primitive.ObjectID, len(pickElements))
for i, v := range pickElements {
objectIDs[i] = v.(primitive.ObjectID)
}
// 获取视频详情
var videos []*vidmod.VideoModel
if videos, err = vidmod.GetVideoListByIDsPublish(objectIDs); err != nil {
log.Error("vidmod.GetVideoListByIDsPublish", log.Any("videoIDs", vIds), log.E(err))
return
}
if videos == nil || len(videos) == 0 {
log.Warn("vidmod.GetVideoListByIDsPublish no video info", log.Any("videosIDs", vIds))
return
}
resp.Videos = vidhelpser.NewEncodeVideoInfoNotStatusForUser(uid, videos)
return
}
// GetChangeMedias 获取换一批的视频列表
func GetChangeMedias(in *vidmod.ChangeVideoSectionReq, uid uint64) (resp VideoUnderSectionResp, err error) {
sectionID, err := primitive.ObjectIDFromHex(in.SectionID)
if err != nil {
log.Error(fmt.Sprintf("GetChangeVideos primitive objectIDFromHex err:%v", err))
return
}
// 获专题信息
section, err := modulesectionmod.GetBySectionByID(sectionID)
if err != nil {
log.Error(fmt.Sprintf("GetChangeVideos modulesectionmod getBySectionByID err:%v", err))
return
}
if section.ID.IsZero() {
return
}
active, err := moduleconfmod.CanBrowseModule(section.SubModuleID, time.Now())
if err != nil || !active {
return resp, err
}
// 获取专题下视频信息
sort := bson.D{{Key: "sectionSort", Value: -1}, {Key: "createdAt", Value: -1}}
list, _, err := mediamod.GetList(bson.M{"sId": section.ID, "isDelete": false, "status": 1}, 0, 200, sort)
if len(list) < 0 {
return
}
// 根据样式判断随机数据
num := modulesectionmod.ShowTypeHashVale[section.ShowType]
if len(list) < num {
return
}
if num == 0 {
num = 9
}
rp := common.NewRandomPicker()
pickElements, err := rp.PickElements(list, num)
if err != nil {
return
}
mediaList := []*mediamod.Media{}
for _, v := range pickElements {
item, ok := v.(*mediamod.Media)
if !ok {
continue
}
mediaList = append(mediaList, item)
}
resp.Medias = mediaser.FillNotStatusMedias(mediaList, false)
return
}
func MostNewModuleVideoList(uid uint64, req vidmod.HomeMostNewModuleVideoListReq) (ret AllVideosOfModuleResp, err error) {
defer func() {
vidhelpser.ApplyFreeTrialBadgeToVideoInfos(vidhelpser.LoadFreeTrialBadgeContext(uid), ret.Videos)
}()
now := time.Now()
excludedModules, excludeErr := moduleconfmod.ExcludedVideoModuleIDs(now, false)
if excludeErr != nil {
return ret, excludeErr
}
useCache := len(excludedModules) == 0
if useCache {
key := redisconst.GetMostNewModuleVideoListKey(req.SortType, req.PageNumber, req.PageSize)
err = appg.Redis.GetWithJson2Any(key, &ret)
if err == nil {
return
}
if !errors.Is(err, goredis.Nil) {
log.Error("appg.Redis.GetWithJson2Any error", log.E(err))
}
defer func() {
appg.Redis.SetWithAny2Json(key, ret, redisconst.MostNewModuleVideoListExpire)
}()
}
// 获取暗网标签
tag, _ := tagmod.FindOneTagByName("暗网")
cond := bson.M{"status": 1, "newsType": vidmod.SP}
if len(excludedModules) > 0 {
cond["mId"] = bson.M{"$nin": excludedModules}
}
if !tag.ID.IsZero() {
cond["tags"] = bson.M{"$ne": tag.ID}
}
var sort bson.D
switch req.SortType {
case 1: // 最新强制 在2年内的帖子
if req.PageNumber > 100 {
return
}
cond["reviewAt"] = bson.M{"$gte": now.AddDate(-2, 0, 0)}
sort = bson.D{{Key: "reviewAt", Value: -1}}
case 2: // 本周 15天内上架 播放数
cond["reviewAt"] = bson.M{"$gte": now.AddDate(0, 0, -15)}
sort = bson.D{{Key: "playCount", Value: -1}, {Key: "reviewAt", Value: -1}}
case 3: // 月度
cond["reviewAt"] = bson.M{"$lt": now.AddDate(0, 0, -15), "$gte": now.AddDate(0, -1, -15)}
sort = bson.D{{Key: "playCount", Value: -1}, {Key: "reviewAt", Value: -1}}
case 4: // 年度
cond["reviewAt"] = bson.M{"$lt": now.AddDate(0, -1, -15), "$gte": now.AddDate(0, -9, 0)}
sort = bson.D{{Key: "playCount", Value: -1}, {Key: "reviewAt", Value: -1}}
default:
cond["reviewAt"] = bson.M{"$lt": now.AddDate(0, -1, -15), "$gte": now.AddDate(0, -9, 0)}
sort = bson.D{{Key: "playCount", Value: -1}, {Key: "reviewAt", Value: -1}}
}
opt := options.Find().SetSort(sort).SetSkip(req.Skip64()).SetLimit(req.Limit64())
videos, next, err := vidmod.FindList(cond, opt)
if err != nil {
return ret, err
}
vInfos := vidhelpser.EncodeVideoInfoNoUID(videos)
ret.Videos = vInfos
ret.HasNext = next
return ret, nil
}