1419 lines
41 KiB
Go
1419 lines
41 KiB
Go
package vidhelpser
|
||
|
||
import (
|
||
"91porn-server/app/service/searchaccessser"
|
||
"91porn-server/models/v/cmtmod"
|
||
"91porn-server/models/v/moduleconfmod"
|
||
"math"
|
||
"sync"
|
||
"time"
|
||
|
||
"91porn-server/app/appg"
|
||
"91porn-server/app/service/walletser"
|
||
"91porn-server/common"
|
||
"91porn-server/common/constant/redisconst"
|
||
"91porn-server/common/log"
|
||
"91porn-server/common/maths"
|
||
"91porn-server/common/redis"
|
||
"91porn-server/common/timeutil/timerange"
|
||
"91porn-server/models/l/payvidlgmod"
|
||
"91porn-server/models/s/statrecordmod"
|
||
"91porn-server/models/v/collectmod"
|
||
"91porn-server/models/v/discount_area_mod"
|
||
"91porn-server/models/v/followmod"
|
||
"91porn-server/models/v/freeVidmod"
|
||
"91porn-server/models/v/likemod"
|
||
"91porn-server/models/v/locmod"
|
||
"91porn-server/models/v/tagmod"
|
||
"91porn-server/models/v/usermod"
|
||
"91porn-server/models/v/videodiscountmod"
|
||
"91porn-server/models/v/vidmod"
|
||
"91porn-server/skd/skdg"
|
||
|
||
"github.com/shopspring/decimal"
|
||
"go.mongodb.org/mongo-driver/bson"
|
||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||
)
|
||
|
||
// OID 别名
|
||
type OID = primitive.ObjectID
|
||
|
||
const (
|
||
//MaxNewestCacheNum 最新帖子最大缓存条数
|
||
MaxNewestCacheNum = 500
|
||
// 视频点赞数放大倍数
|
||
VideoLikeCountMultiplier = 33
|
||
// 视频播放量放大倍数
|
||
VideoPlayCountMultiplier = 99
|
||
)
|
||
|
||
// GetLocationByIP 通过ip获取地理位置
|
||
func GetLocationByIP(ip string) string {
|
||
r := getRedis()
|
||
city, err := r.GetString(redisconst.IpLocationFmt(ip))
|
||
if city != "" && err == nil {
|
||
return city
|
||
}
|
||
city, _ = common.GetLocationByIP(ip)
|
||
_ = r.Set(redisconst.IpLocationFmt(ip), city, redisconst.IPLocationExpireMax)
|
||
return city
|
||
}
|
||
|
||
func addUserInfo(srcMap map[uint64]*vidmod.Publisher, add []*usermod.BaseInfoVip) map[uint64]*vidmod.Publisher {
|
||
if srcMap == nil {
|
||
srcMap = make(map[uint64]*vidmod.Publisher)
|
||
}
|
||
for _, info := range add {
|
||
p := vidmod.Publisher{
|
||
UInfo: *info,
|
||
}
|
||
srcMap[info.UID] = &p
|
||
}
|
||
return srcMap
|
||
}
|
||
|
||
// GetUserInfo2Map 获取用户基本信息到map
|
||
func GetUserInfo2Map(uid uint64, uids []uint64) map[uint64]*vidmod.Publisher {
|
||
m, unExistsUID, err := getUsersBaseInfoFromRedis(uids)
|
||
if err != nil || len(unExistsUID) != 0 {
|
||
unExistsUsers, err := getUsersBaseInfoFromMongo(unExistsUID)
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
common.Go(func() {
|
||
_ = setUsersBaseInfo2Redis(unExistsUsers)
|
||
})
|
||
m = addUserInfo(m, unExistsUsers)
|
||
}
|
||
var wg sync.WaitGroup
|
||
var followMap map[uint64]bool
|
||
// var rchgLevelMap map[uint64]usermod.RechargeLevel
|
||
var userWorks []vidmod.WorkCount
|
||
wg.Add(2)
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
followMap, _ = followmod.IsFollowUsers(uid, uids)
|
||
})
|
||
// common.Go(func() {
|
||
// defer wg.Done()
|
||
// rchgLevelMap, _ = walletser.GetUidsRchgLevel(uids)
|
||
// })
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
var filter = bson.M{}
|
||
filter["publisherID"] = bson.M{"$in": uids}
|
||
filter["status"] = vidmod.CheckPass
|
||
userWorks, _ = vidmod.TotalWorks(filter)
|
||
})
|
||
wg.Wait()
|
||
|
||
for u, p := range m {
|
||
p.HasFollowed = followMap[u]
|
||
// p.RechargeLevel = rchgLevelMap[u].Level
|
||
for _, w := range userWorks {
|
||
if p.UID == w.Uid {
|
||
p.TotalWorks = w.Count
|
||
}
|
||
}
|
||
}
|
||
return m
|
||
}
|
||
|
||
// GetUserInfo2MapNoUID 获取用户基本信息到map
|
||
func GetUserInfo2MapNoUID(uids []uint64) map[uint64]*vidmod.Publisher {
|
||
m, unExistsUID, err := getUsersBaseInfoFromRedis(uids)
|
||
if err != nil || len(unExistsUID) != 0 {
|
||
unExistsUsers, err := getUsersBaseInfoFromMongo(unExistsUID)
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
common.Go(func() {
|
||
_ = setUsersBaseInfo2Redis(unExistsUsers)
|
||
})
|
||
m = addUserInfo(m, unExistsUsers)
|
||
}
|
||
var rchgLevelMap map[uint64]usermod.RechargeLevel
|
||
rchgLevelMap, _ = walletser.GetUidsRchgLevel(uids)
|
||
for u, p := range m {
|
||
p.RechargeLevel = rchgLevelMap[u].Level
|
||
}
|
||
return m
|
||
}
|
||
|
||
// GetSimpleUserMapNoUID 获取用户基本信息到map
|
||
func GetSimpleUserMapNoUID(uids []uint64) map[uint64]*vidmod.Publisher {
|
||
m, unExistsUID, err := getUsersBaseInfoFromRedis(uids)
|
||
if err != nil || len(unExistsUID) != 0 {
|
||
unExistsUsers, err := getUsersBaseInfoFromMongo(unExistsUID)
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
common.Go(func() {
|
||
_ = setUsersBaseInfo2Redis(unExistsUsers)
|
||
})
|
||
m = addUserInfo(m, unExistsUsers)
|
||
}
|
||
return m
|
||
}
|
||
|
||
// GetUserStatus 获取 用户状态信息
|
||
func GetUserStatus(uid uint64, videoIDs []OID) map[OID]vidmod.VideoStatus {
|
||
mLike := make(map[primitive.ObjectID]bool)
|
||
mPay := make(map[primitive.ObjectID]bool)
|
||
mCollect := make(map[primitive.ObjectID]bool)
|
||
var wg sync.WaitGroup
|
||
wg.Add(3)
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
mLike, _ = likemod.IsLikeVideos(uid, videoIDs)
|
||
})
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
mPay, _ = payvidlgmod.IsPay4Videos(uid, videoIDs)
|
||
})
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
mCollect, _ = collectmod.IsCollectVideos(uid, videoIDs)
|
||
})
|
||
wg.Wait()
|
||
m := make(map[OID]vidmod.VideoStatus)
|
||
for _, v := range videoIDs {
|
||
info := vidmod.VideoStatus{
|
||
HasLiked: mLike[v],
|
||
HasPaid: mPay[v],
|
||
HasCollected: mCollect[v],
|
||
TodayRank: 0,
|
||
TodayPlayCnt: 0,
|
||
}
|
||
m[v] = info
|
||
}
|
||
return m
|
||
}
|
||
|
||
// GetTagsByIDs 通过tagid列表获取tag详情列表
|
||
func GetTagsByIDs(ids []OID) ([]vidmod.TagInfo, bool) {
|
||
tags, unExistsTid, err := getTagsByIDsFromRedis(ids)
|
||
if err != nil || len(unExistsTid) != 0 {
|
||
unExistsTInfo, err := getTagsByIDsFromMongo(unExistsTid)
|
||
if err != nil {
|
||
return nil, false
|
||
}
|
||
common.Go(func() {
|
||
_ = setTagsByIDs2Redis(unExistsTInfo)
|
||
})
|
||
tags = append(tags, unExistsTInfo...)
|
||
}
|
||
return tags, true
|
||
}
|
||
|
||
// GetTagsByIDs2Map 通过tagid列表获取tag详情列表
|
||
func GetTagsByIDs2Map(ids []OID) map[primitive.ObjectID]vidmod.TagInfo {
|
||
m := make(map[primitive.ObjectID]vidmod.TagInfo)
|
||
tags, ok := GetTagsByIDs(ids)
|
||
if !ok {
|
||
return m
|
||
}
|
||
for _, info := range tags {
|
||
m[info.ID] = vidmod.TagInfo{
|
||
ID: info.ID,
|
||
Name: info.Name,
|
||
CoverImg: info.CoverImg,
|
||
Description: info.Description,
|
||
}
|
||
}
|
||
return m
|
||
}
|
||
|
||
// GetPartTags 从内存获取tagInfo
|
||
func GetPartTags(ids []OID, mTags map[primitive.ObjectID]vidmod.TagInfo) []vidmod.TagInfo {
|
||
infos := make([]vidmod.TagInfo, 0, len(ids))
|
||
for _, i := range ids {
|
||
if !mTags[i].ID.IsZero() {
|
||
infos = append(infos, mTags[i])
|
||
}
|
||
}
|
||
return infos
|
||
}
|
||
|
||
func getVideoParentCommentCountMap(objIDs []primitive.ObjectID) map[primitive.ObjectID]int {
|
||
res := make(map[primitive.ObjectID]int, len(objIDs))
|
||
countMap, err := cmtmod.CmtFindParentCountByObjIDs(cmtmod.CmtTypeVideo, objIDs)
|
||
if err != nil {
|
||
return res
|
||
}
|
||
for id, cnt := range countMap {
|
||
res[id] = int(cnt)
|
||
}
|
||
return res
|
||
}
|
||
|
||
// Transfer2Info 将视频数据库模式转为返回模式
|
||
func transfer2Info(v *vidmod.VideoModel, uInfo vidmod.Publisher, vStatus vidmod.VideoStatus, loc locmod.Location, ti []vidmod.TagInfo, com vidmod.CommentInfo, originCoins int64) *vidmod.VideoInfo {
|
||
if v.Status == 3 {
|
||
v.Status = 1
|
||
}
|
||
newType := vidmod.SP
|
||
if len(v.NewsType) != 0 {
|
||
newType = v.NewsType
|
||
}
|
||
base := vidmod.VideoBase{
|
||
ID: v.ID,
|
||
NewsType: newType,
|
||
Title: v.Title,
|
||
Content: v.Content,
|
||
Tags: ti,
|
||
PlayTime: v.PlayTime,
|
||
Cover: v.Cover,
|
||
CoverThumb: v.CoverThumb,
|
||
SeriesCover: v.SeriesCover,
|
||
PlayCount: (v.PlayCount + v.FakePlayCount) * VideoPlayCountMultiplier,
|
||
LikeCount: v.LikeCount, // 移除点赞数放大倍数,修复各处点赞数不一致问题
|
||
PageViewCount: v.PageViewCount * 3,
|
||
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: H265URLForApp(v),
|
||
PreviewURL: v.PreviewURL,
|
||
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: originCoins,
|
||
CollectCount: v.CollectCount,
|
||
SeedLinkUrl: v.SeedLinkUrl,
|
||
SeedSize: v.SeedSize,
|
||
SeedPlayTime: v.SeedPlayTime,
|
||
RichText: v.RichText,
|
||
PreviewStart: v.PreviewStart,
|
||
TimeNodeList: v.TimeNodeList,
|
||
DownloadAllow: v.DownloadAllow,
|
||
ShowType: v.ShowType,
|
||
}
|
||
if v.Via == "laosiji" {
|
||
base.LsjId = v.SourceID
|
||
}
|
||
locInfo := vidmod.LocInfo{
|
||
ID: loc.ID,
|
||
City: loc.City,
|
||
Cover: loc.Cover,
|
||
Visit: loc.FakeVisit,
|
||
CreatedAt: loc.CreatedAt,
|
||
}
|
||
info := vidmod.VideoInfo{
|
||
VideoBase: base,
|
||
UInfo: uInfo,
|
||
Location: locInfo,
|
||
VidStatus: vStatus,
|
||
Comment: com,
|
||
}
|
||
return &info
|
||
}
|
||
|
||
// EncodeVideoInfoBlogger 视频上传博主查看自己上传视频(临时解决)
|
||
func EncodeVideoInfoBlogger(uid uint64, infos []*vidmod.VideoModel) []*vidmod.VideoInfoResp {
|
||
infos = filterOutsideSearch(infos)
|
||
infosLen := len(infos)
|
||
back := make([]*vidmod.VideoInfoResp, infosLen)
|
||
if infosLen == 0 {
|
||
return back
|
||
}
|
||
uids := make([]uint64, infosLen)
|
||
videos := make([]primitive.ObjectID, infosLen)
|
||
lids := make([]primitive.ObjectID, infosLen)
|
||
var tags []primitive.ObjectID
|
||
|
||
for i, v := range infos {
|
||
uids[i] = v.PublisherID
|
||
videos[i] = v.ID
|
||
lids[i] = v.Location
|
||
tags = append(tags, v.Tags...)
|
||
}
|
||
var (
|
||
mUser map[uint64]*vidmod.Publisher
|
||
mStatus map[OID]vidmod.VideoStatus
|
||
mTags map[primitive.ObjectID]vidmod.TagInfo
|
||
wg sync.WaitGroup
|
||
)
|
||
|
||
wg.Add(3)
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
mUser = GetUserInfo2Map(uid, uids)
|
||
})
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
mStatus = GetUserStatus(uid, videos)
|
||
})
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
mTags = GetTagsByIDs2Map(tags)
|
||
})
|
||
wg.Wait()
|
||
commentCountMap := getVideoParentCommentCountMap(videos)
|
||
for i, v := range infos {
|
||
originCoins := v.Coins
|
||
var uInfo vidmod.Publisher
|
||
if mUser[v.PublisherID] != nil {
|
||
uInfo = *mUser[v.PublisherID]
|
||
}
|
||
back[i] = NewTransfer2Info(v, uInfo, mStatus[v.ID], GetPartTags(v.Tags, mTags), originCoins)
|
||
back[i].CommentCount = commentCountMap[v.ID]
|
||
if v.Status != vidmod.CheckPass && v.PublisherID == uid {
|
||
back[i].PageViewCount = 0
|
||
}
|
||
}
|
||
return back
|
||
}
|
||
|
||
// EncodeVideoInfo 封装返回的视频列表信息
|
||
func EncodeVideoInfo(uid uint64, infos []*vidmod.VideoModel) []*vidmod.VideoInfo {
|
||
return encodeVideoInfo(uid, filterOutsideSearch(infos))
|
||
}
|
||
|
||
// EncodeVideoInfoForRecommend 过滤配置为不进入推荐场景的亚模块内容。
|
||
func EncodeVideoInfoForRecommend(uid uint64, infos []*vidmod.VideoModel) []*vidmod.VideoInfo {
|
||
return encodeVideoInfo(uid, filterRecommend(infos))
|
||
}
|
||
|
||
// EncodeVideoInfoForSearch 保留搜索专属内容,并给结果签发短时效访问凭证。
|
||
func EncodeVideoInfoForSearch(uid uint64, infos []*vidmod.VideoModel) []*vidmod.VideoInfo {
|
||
result := encodeVideoInfo(uid, filterSearch(infos))
|
||
attachVideoInfoSearchTokens(uid, result)
|
||
return result
|
||
}
|
||
|
||
func encodeVideoInfo(uid uint64, infos []*vidmod.VideoModel) []*vidmod.VideoInfo {
|
||
if uid == 0 {
|
||
return encodeVideoInfoNoUID(infos)
|
||
}
|
||
infosLen := len(infos)
|
||
//now := time.Now()
|
||
back := make([]*vidmod.VideoInfo, infosLen)
|
||
if infosLen == 0 {
|
||
return back
|
||
}
|
||
uids := make([]uint64, infosLen)
|
||
videos := make([]primitive.ObjectID, infosLen)
|
||
lids := make([]primitive.ObjectID, infosLen)
|
||
discountAreaIds := []primitive.ObjectID{}
|
||
var tags []primitive.ObjectID
|
||
mids := []primitive.ObjectID{}
|
||
for i, v := range infos {
|
||
uids[i] = v.PublisherID
|
||
videos[i] = v.ID
|
||
lids[i] = v.Location
|
||
if !v.DiscountAreaId.IsZero() {
|
||
discountAreaIds = append(discountAreaIds, v.DiscountAreaId)
|
||
}
|
||
tags = append(tags, v.Tags...)
|
||
mid, _ := primitive.ObjectIDFromHex(v.MID)
|
||
if !mid.IsZero() {
|
||
mids = append(mids, mid)
|
||
}
|
||
|
||
}
|
||
var mUser map[uint64]*vidmod.Publisher
|
||
var mStatus map[OID]vidmod.VideoStatus
|
||
var mLocation map[primitive.ObjectID]locmod.Location
|
||
var mComment map[primitive.ObjectID]vidmod.CommentInfo
|
||
var mTags map[primitive.ObjectID]vidmod.TagInfo
|
||
var mFree map[uint64]bool
|
||
discountAreaMap := make(map[primitive.ObjectID]*discount_area_mod.DiscountArea)
|
||
var self *usermod.User
|
||
var videoDiscLog videodiscountmod.VideoDiscountLog
|
||
var largestDiscount int
|
||
moduleMap := make(map[string]moduleconfmod.ModuleConf)
|
||
var wg sync.WaitGroup
|
||
wg.Add(9)
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
moduleList, err := moduleconfmod.FindByIDs(mids)
|
||
if err != nil {
|
||
log.Error("EncodeVideoInfo: moduleconfmod.FindByIDs fail", log.E(err))
|
||
return
|
||
}
|
||
for _, v := range moduleList {
|
||
moduleMap[v.ID.Hex()] = v
|
||
}
|
||
})
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
// 通过折扣专区id获取专区
|
||
vidDiscountArea, err := discount_area_mod.GetDiscountAreaByIds(discountAreaIds)
|
||
if err != nil {
|
||
log.Error("EncodeVideoInfo: discount_area_mod.GetDiscountAreaByIds", log.Any("discountAreaIds", discountAreaIds), log.E(err))
|
||
return
|
||
}
|
||
for _, v := range vidDiscountArea {
|
||
discountAreaMap[v.ID] = &v
|
||
}
|
||
})
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
user, findErr := usermod.FindUserByUID(uid)
|
||
if findErr != nil {
|
||
log.Error("EncodeVideoInfo: usermod.FindUserByUID", log.Any("uid", uid), log.E(findErr))
|
||
return
|
||
}
|
||
self = user
|
||
//if self != nil && !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()
|
||
mUser = GetUserInfo2Map(uid, uids)
|
||
})
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
mStatus = GetUserStatus(uid, videos)
|
||
})
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
//mLocation, _ = locmod.GetLocationInfoByIDs(lids)
|
||
mLocation = make(map[primitive.ObjectID]locmod.Location)
|
||
})
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
mTags = GetTagsByIDs2Map(tags)
|
||
})
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
mFree = freeVidmod.GetFreeVideoMapByUID(uid)
|
||
})
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
discountLog, findErr := videodiscountmod.GetByUID(uid)
|
||
if findErr != nil {
|
||
log.Error("videodiscountmod.GetByUID", log.Any("uid", uid), log.E(findErr))
|
||
return
|
||
}
|
||
videoDiscLog = discountLog
|
||
})
|
||
wg.Wait()
|
||
freeTrialContext := FreeTrialBadgeContextForUser(uid, self)
|
||
commentCountMap := getVideoParentCommentCountMap(videos)
|
||
for i, v := range infos {
|
||
var uInfo vidmod.Publisher
|
||
if mUser[v.PublisherID] != nil {
|
||
uInfo = *mUser[v.PublisherID]
|
||
} else {
|
||
//处理作者被删除的情况
|
||
if len(mUser) > 0 {
|
||
for i := range mUser {
|
||
uInfo = *mUser[i]
|
||
break
|
||
}
|
||
}
|
||
}
|
||
// 金币帖子 - 图片帖子对所有人原价
|
||
// 非图片帖子再按规则计算折扣价
|
||
originCoins := v.Coins
|
||
infos[i].Coins = originCoins
|
||
if v.NewsType != vidmod.COVER {
|
||
infos[i].Coins = CalcVideoCoins(v.Coins, v.PublisherID, self, mFree, videoDiscLog, largestDiscount, discountAreaMap[v.DiscountAreaId])
|
||
}
|
||
info := transfer2Info(v, uInfo, mStatus[v.ID], mLocation[v.Location], GetPartTags(v.Tags, mTags), mComment[v.ID], originCoins)
|
||
if v.NewsType != vidmod.COVER {
|
||
// 计算折扣专区价格
|
||
info.DiscountAreaPrice = CalcDiscountAreaPrice(originCoins, discountAreaMap[v.DiscountAreaId])
|
||
}
|
||
info.ShowFreeTrialBadge, info.FreeTrialRemaining, info.CanUseFreeTrial = freeTrialContext.Fields(
|
||
info.NewsType,
|
||
originCoins,
|
||
v.FreeArea,
|
||
v.PublisherID,
|
||
)
|
||
info.HappinessPlazaTop = infos[i].HappinessPlazaTop
|
||
back[i] = info
|
||
module, ok := moduleMap[v.MID]
|
||
if ok {
|
||
info.VideoTypeId = module.ID.Hex()
|
||
info.VideoTypeName = module.ModuleName
|
||
}
|
||
// 与 comment/list 口径一致:不统计置顶公共评论(该评论不在评论表内)。
|
||
back[i].CommentCount = commentCountMap[info.ID]
|
||
}
|
||
common.Go(func() {
|
||
incVideoPageView(videos)
|
||
})
|
||
return back
|
||
}
|
||
|
||
// NewEncodeVideoInfoNotStatus 封装返回的视频列表信息
|
||
func NewEncodeVideoInfoNotStatus(infos []*vidmod.VideoModel) []*vidmod.VideoInfoResp {
|
||
return newEncodeVideoInfoNotStatus(filterOutsideSearch(infos))
|
||
}
|
||
|
||
// NewEncodeVideoInfoNotStatusForUser 封装需要用户免费观看状态的无状态视频列表。
|
||
func NewEncodeVideoInfoNotStatusForUser(uid uint64, infos []*vidmod.VideoModel) []*vidmod.VideoInfoResp {
|
||
result := newEncodeVideoInfoNotStatus(filterOutsideSearch(infos))
|
||
ApplyFreeTrialBadgeToVideoInfoResps(LoadFreeTrialBadgeContext(uid), result)
|
||
return result
|
||
}
|
||
|
||
// NewEncodeVideoInfoNotStatusForRecommend 过滤配置为不进入推荐场景的亚模块内容。
|
||
func NewEncodeVideoInfoNotStatusForRecommend(infos []*vidmod.VideoModel) []*vidmod.VideoInfoResp {
|
||
return newEncodeVideoInfoNotStatus(filterRecommend(infos))
|
||
}
|
||
|
||
// NewEncodeVideoInfoNotStatusForSearch 保留搜索专属内容,并给结果签发短时效访问凭证。
|
||
func NewEncodeVideoInfoNotStatusForSearch(uid uint64, infos []*vidmod.VideoModel) []*vidmod.VideoInfoResp {
|
||
result := newEncodeVideoInfoNotStatus(filterSearch(infos))
|
||
ApplyFreeTrialBadgeToVideoInfoResps(LoadFreeTrialBadgeContext(uid), result)
|
||
attachVideoInfoRespSearchTokens(uid, result)
|
||
return result
|
||
}
|
||
|
||
func newEncodeVideoInfoNotStatus(infos []*vidmod.VideoModel) []*vidmod.VideoInfoResp {
|
||
if len(infos) == 0 {
|
||
return nil
|
||
}
|
||
var (
|
||
back []*vidmod.VideoInfoResp
|
||
uids []uint64
|
||
videos []primitive.ObjectID
|
||
lids []primitive.ObjectID
|
||
tags []primitive.ObjectID
|
||
|
||
mUser = map[uint64]*vidmod.Publisher{}
|
||
mTags = map[primitive.ObjectID]vidmod.TagInfo{}
|
||
mStatus = map[OID]vidmod.VideoStatus{}
|
||
)
|
||
for _, i := range infos {
|
||
uids = append(uids, i.PublisherID)
|
||
videos = append(videos, i.ID)
|
||
lids = append(lids, i.Location)
|
||
tags = append(tags, i.Tags...)
|
||
}
|
||
|
||
var wg sync.WaitGroup
|
||
wg.Add(2)
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
mUser = GetUserInfo2Map(0, uids)
|
||
})
|
||
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
mTags = GetTagsByIDs2Map(tags)
|
||
})
|
||
|
||
wg.Wait()
|
||
commentCountMap := getVideoParentCommentCountMap(videos)
|
||
for i, v := range infos {
|
||
temp := v
|
||
originCoins := temp.Coins
|
||
var uInfo vidmod.Publisher
|
||
if mUser[temp.PublisherID] != nil {
|
||
uInfo = *mUser[temp.PublisherID]
|
||
} else {
|
||
//处理作者被删除的情况
|
||
if len(mUser) > 0 {
|
||
for i := range mUser {
|
||
uInfo = *mUser[i]
|
||
break
|
||
}
|
||
}
|
||
}
|
||
infos[i].Coins = originCoins
|
||
info := NewTransfer2Info(temp, uInfo, mStatus[temp.ID], GetPartTags(temp.Tags, mTags), originCoins)
|
||
info.CommentCount = commentCountMap[temp.ID]
|
||
back = append(back, info)
|
||
}
|
||
common.Go(func() {
|
||
incVideoPageView(videos)
|
||
})
|
||
return back
|
||
}
|
||
|
||
// NewEncodeVideoInfo 封装返回的视频列表信息
|
||
func NewEncodeVideoInfo(uid uint64, infos []*vidmod.VideoModel) []*vidmod.VideoInfoResp {
|
||
return newEncodeVideoInfo(uid, filterOutsideSearch(infos))
|
||
}
|
||
|
||
func newEncodeVideoInfo(uid uint64, infos []*vidmod.VideoModel) []*vidmod.VideoInfoResp {
|
||
var back []*vidmod.VideoInfoResp
|
||
if uid == 0 {
|
||
return back
|
||
}
|
||
if len(infos) == 0 {
|
||
return back
|
||
}
|
||
var uids []uint64
|
||
var videos []primitive.ObjectID
|
||
var lids []primitive.ObjectID
|
||
var tags []primitive.ObjectID
|
||
for _, i := range infos {
|
||
uids = append(uids, i.PublisherID)
|
||
videos = append(videos, i.ID)
|
||
lids = append(lids, i.Location)
|
||
tags = append(tags, i.Tags...)
|
||
|
||
}
|
||
var (
|
||
mUser map[uint64]*vidmod.Publisher
|
||
mStatus map[OID]vidmod.VideoStatus
|
||
mTags map[primitive.ObjectID]vidmod.TagInfo
|
||
)
|
||
|
||
var wg sync.WaitGroup
|
||
wg.Add(3)
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
mUser = GetUserInfo2Map(uid, uids)
|
||
})
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
mStatus = GetUserStatus(uid, videos)
|
||
})
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
mTags = GetTagsByIDs2Map(tags)
|
||
})
|
||
wg.Wait()
|
||
freeTrialContext := LoadFreeTrialBadgeContext(uid)
|
||
commentCountMap := getVideoParentCommentCountMap(videos)
|
||
for i, v := range infos {
|
||
temp := v
|
||
originCoins := temp.Coins
|
||
var uInfo vidmod.Publisher
|
||
if mUser[temp.PublisherID] != nil {
|
||
uInfo = *mUser[temp.PublisherID]
|
||
} else {
|
||
//处理作者被删除的情况
|
||
if len(mUser) > 0 {
|
||
for i := range mUser {
|
||
uInfo = *mUser[i]
|
||
break
|
||
}
|
||
}
|
||
}
|
||
infos[i].Coins = originCoins
|
||
info := NewTransfer2Info(temp, uInfo, mStatus[temp.ID], GetPartTags(v.Tags, mTags), originCoins)
|
||
info.ShowFreeTrialBadge, info.FreeTrialRemaining, info.CanUseFreeTrial = freeTrialContext.Fields(
|
||
info.NewsType,
|
||
originCoins,
|
||
temp.FreeArea,
|
||
temp.PublisherID,
|
||
)
|
||
info.CommentCount = commentCountMap[temp.ID]
|
||
back = append(back, info)
|
||
}
|
||
common.Go(func() {
|
||
incVideoPageView(videos)
|
||
})
|
||
return back
|
||
}
|
||
|
||
// CalcVideoCoins 计算折扣后视频金币数,判断了包括用户是否为VIP,以及是否购买了视频折扣卡等状态
|
||
// 注意:调用此函数时,在用户为VIP的情况下,largestDiscount必须设为0!
|
||
func CalcVideoCoins(originCoins int64, publisherID uint64, self *usermod.User, mFree map[uint64]bool,
|
||
videoDiscLog videodiscountmod.VideoDiscountLog, largestDiscount int, discountArea *discount_area_mod.DiscountArea) (discountedCoins int64) {
|
||
if originCoins == 0 {
|
||
discountedCoins = 0
|
||
return
|
||
}
|
||
discountedCoins = math.MaxInt64
|
||
now := time.Now()
|
||
// 如果在折扣专区里,直接按照折扣专区的价格来
|
||
if self != nil && self.IsVIP(now) && discountArea != nil {
|
||
// 计算折扣专区价格
|
||
discountedCoins = CalcDiscountAreaPrice(originCoins, discountArea)
|
||
return
|
||
}
|
||
//判断当前视频 是否对uid为免费视频 则将金币置为0
|
||
if b, ok := mFree[publisherID]; ok && b {
|
||
discountedCoins = 0
|
||
return
|
||
}
|
||
goldVideoFreeLimit := usermod.GetGoldVideoFreeLimit(self)
|
||
// 预售卡全部金币视频免费
|
||
if self != nil && self.AllGoldVideoFree && self.GoldVideoFreeExpire.After(now) {
|
||
discountedCoins = 0
|
||
return
|
||
}
|
||
// 用户购买了视频免费卡(视频金币数少于30)
|
||
if originCoins <= goldVideoFreeLimit && self != nil && self.VideoFreeExpiration != nil && self.VideoFreeExpiration.After(now) {
|
||
discountedCoins = 0
|
||
return
|
||
}
|
||
|
||
// 用户购买VIP时赠送了视频免费天数
|
||
if self != nil && originCoins > 0 && originCoins <= goldVideoFreeLimit && (self.GoldVideoFreeExpire.After(now) ||
|
||
(self.VideoFreeExpiration != nil && self.VideoFreeExpiration.After(now))) {
|
||
discountedCoins = 0
|
||
return
|
||
}
|
||
// 用户购买VIP后赠送视频折扣
|
||
if originCoins > 0 && self != nil && self.IsVIP(now) && self.PayVidDiscount > 0 {
|
||
discountedCoins = decimal.NewFromInt(int64(self.PayVidDiscount)).Shift(-1).
|
||
Mul(decimal.NewFromInt(originCoins)).Round(0).IntPart()
|
||
}
|
||
if originCoins > 0 {
|
||
if videoDiscLog.VideoDiscount > 0 { // 用户购买了视频折扣卡
|
||
newDiscountedCoins := decimal.NewFromInt(int64(videoDiscLog.VideoDiscount)).Shift(-1).
|
||
Mul(decimal.NewFromInt(originCoins)).Round(0).IntPart()
|
||
if newDiscountedCoins < discountedCoins {
|
||
discountedCoins = newDiscountedCoins
|
||
}
|
||
} else if largestDiscount > 0 { // 用户未购买视频折扣卡,此时按系统中所有VIP卡最大折扣计算
|
||
newDiscountedCoins := decimal.NewFromInt(int64(largestDiscount)).Shift(-1).
|
||
Mul(decimal.NewFromInt(originCoins)).Round(0).IntPart()
|
||
if newDiscountedCoins < discountedCoins {
|
||
discountedCoins = newDiscountedCoins
|
||
}
|
||
}
|
||
}
|
||
if discountedCoins == math.MaxInt64 {
|
||
discountedCoins = originCoins
|
||
}
|
||
return
|
||
}
|
||
|
||
// CalcDiscountAreaPrice 计算折扣专区的价格
|
||
func CalcDiscountAreaPrice(originCoins int64, discountArea *discount_area_mod.DiscountArea) (price int64) {
|
||
price = originCoins
|
||
// 判断当前视频是否在vip折扣区
|
||
if discountArea != nil {
|
||
price = decimal.NewFromInt(int64(discountArea.Discount)).Shift(-2).
|
||
Mul(decimal.NewFromInt(originCoins)).Round(0).IntPart()
|
||
}
|
||
return
|
||
}
|
||
|
||
// EncodeVideoInfoNoUID 封装返回的视频列表信息(无用户)
|
||
func EncodeVideoInfoNoUID(infos []*vidmod.VideoModel) []*vidmod.VideoInfo {
|
||
return encodeVideoInfoNoUID(filterOutsideSearch(infos))
|
||
}
|
||
|
||
// EncodeVideoInfoNoUIDForRecommend 过滤配置为不进入推荐场景的亚模块内容。
|
||
func EncodeVideoInfoNoUIDForRecommend(infos []*vidmod.VideoModel) []*vidmod.VideoInfo {
|
||
return encodeVideoInfoNoUID(filterRecommend(infos))
|
||
}
|
||
|
||
func encodeVideoInfoNoUID(infos []*vidmod.VideoModel) []*vidmod.VideoInfo {
|
||
infosLen := len(infos)
|
||
back := make([]*vidmod.VideoInfo, infosLen)
|
||
if infosLen == 0 {
|
||
return back
|
||
}
|
||
uids := make([]uint64, infosLen)
|
||
videos := make([]primitive.ObjectID, infosLen)
|
||
lids := make([]primitive.ObjectID, infosLen)
|
||
var tags []primitive.ObjectID
|
||
for i, v := range infos {
|
||
uids[i] = v.PublisherID
|
||
videos[i] = v.ID
|
||
lids[i] = v.Location
|
||
tags = append(tags, v.Tags...)
|
||
}
|
||
var mUser map[uint64]*vidmod.Publisher
|
||
var mLocation map[primitive.ObjectID]locmod.Location
|
||
var mComment map[primitive.ObjectID]vidmod.CommentInfo
|
||
var mTags map[primitive.ObjectID]vidmod.TagInfo
|
||
var wg sync.WaitGroup
|
||
wg.Add(3)
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
mUser = GetUserInfo2MapNoUID(uids)
|
||
})
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
mLocation, _ = locmod.GetLocationInfoByIDs(lids)
|
||
})
|
||
common.Go(func() {
|
||
defer wg.Done()
|
||
mTags = GetTagsByIDs2Map(tags)
|
||
})
|
||
wg.Wait()
|
||
commentCountMap := getVideoParentCommentCountMap(videos)
|
||
for i, v := range infos {
|
||
originCoins := v.Coins
|
||
var uInfo vidmod.Publisher
|
||
if mUser[v.PublisherID] != nil {
|
||
uInfo = *mUser[v.PublisherID]
|
||
}
|
||
noUserStatus := vidmod.VideoStatus{}
|
||
info := transfer2Info(v, uInfo, noUserStatus, mLocation[v.Location], GetPartTags(v.Tags, mTags),
|
||
mComment[v.ID], originCoins)
|
||
info.CommentCount = commentCountMap[v.ID]
|
||
back[i] = info
|
||
}
|
||
common.Go(func() {
|
||
incVideoPageView(videos)
|
||
})
|
||
return back
|
||
}
|
||
|
||
func filterOutsideSearch(infos []*vidmod.VideoModel) []*vidmod.VideoModel {
|
||
if len(infos) == 0 {
|
||
return infos
|
||
}
|
||
moduleIDs := make([]string, 0, len(infos))
|
||
for _, video := range infos {
|
||
if video != nil && video.MID != "" {
|
||
moduleIDs = append(moduleIDs, video.MID)
|
||
}
|
||
}
|
||
blocked, err := moduleconfmod.BlockedOutsideSearchModuleIDs(moduleIDs, time.Now())
|
||
if err != nil {
|
||
log.Error("filterOutsideSearch module query failed", log.E(err))
|
||
return []*vidmod.VideoModel{}
|
||
}
|
||
if len(blocked) == 0 {
|
||
return infos
|
||
}
|
||
filtered := make([]*vidmod.VideoModel, 0, len(infos))
|
||
for _, video := range infos {
|
||
if video == nil {
|
||
continue
|
||
}
|
||
if _, exists := blocked[video.MID]; exists {
|
||
continue
|
||
}
|
||
filtered = append(filtered, video)
|
||
}
|
||
return filtered
|
||
}
|
||
|
||
func filterRecommend(infos []*vidmod.VideoModel) []*vidmod.VideoModel {
|
||
if len(infos) == 0 {
|
||
return infos
|
||
}
|
||
excluded, err := moduleconfmod.ExcludedVideoModuleIDs(time.Now(), true)
|
||
if err != nil {
|
||
log.Error("filterRecommend module query failed", log.E(err))
|
||
return []*vidmod.VideoModel{}
|
||
}
|
||
blocked := make(map[string]struct{}, len(excluded))
|
||
for _, moduleID := range excluded {
|
||
blocked[moduleID] = struct{}{}
|
||
}
|
||
if len(blocked) == 0 {
|
||
return infos
|
||
}
|
||
filtered := make([]*vidmod.VideoModel, 0, len(infos))
|
||
for _, video := range infos {
|
||
if video == nil {
|
||
continue
|
||
}
|
||
if _, exists := blocked[video.MID]; exists {
|
||
continue
|
||
}
|
||
filtered = append(filtered, video)
|
||
}
|
||
return filtered
|
||
}
|
||
|
||
func filterSearch(infos []*vidmod.VideoModel) []*vidmod.VideoModel {
|
||
if len(infos) == 0 {
|
||
return infos
|
||
}
|
||
excluded, err := moduleconfmod.ExcludedSearchModuleIDs()
|
||
if err != nil {
|
||
log.Error("filterSearch module query failed", log.E(err))
|
||
return []*vidmod.VideoModel{}
|
||
}
|
||
blocked := make(map[string]struct{}, len(excluded))
|
||
for _, moduleID := range excluded {
|
||
blocked[moduleID] = struct{}{}
|
||
}
|
||
if len(blocked) == 0 {
|
||
return infos
|
||
}
|
||
filtered := make([]*vidmod.VideoModel, 0, len(infos))
|
||
for _, video := range infos {
|
||
if video == nil {
|
||
continue
|
||
}
|
||
if _, exists := blocked[video.MID]; exists {
|
||
continue
|
||
}
|
||
filtered = append(filtered, video)
|
||
}
|
||
return filtered
|
||
}
|
||
|
||
func attachVideoInfoSearchTokens(uid uint64, videos []*vidmod.VideoInfo) {
|
||
now := time.Now()
|
||
for _, video := range videos {
|
||
if video == nil {
|
||
continue
|
||
}
|
||
token, err := searchaccessser.Issue(uid, video.ID.Hex(), now)
|
||
if err != nil {
|
||
log.Error("issue video search access token failed", log.Any("videoID", video.ID), log.E(err))
|
||
continue
|
||
}
|
||
video.SearchAccessToken = token
|
||
}
|
||
}
|
||
|
||
func attachVideoInfoRespSearchTokens(uid uint64, videos []*vidmod.VideoInfoResp) {
|
||
now := time.Now()
|
||
for _, video := range videos {
|
||
if video == nil {
|
||
continue
|
||
}
|
||
token, err := searchaccessser.Issue(uid, video.ID.Hex(), now)
|
||
if err != nil {
|
||
log.Error("issue video search access token failed", log.Any("videoID", video.ID), log.E(err))
|
||
continue
|
||
}
|
||
video.SearchAccessToken = token
|
||
}
|
||
}
|
||
|
||
func incVideoPageView(vids []primitive.ObjectID) {
|
||
now := time.Now()
|
||
recentMinute := timerange.RecentMinute(now, statrecordmod.FiveMinuteScale)
|
||
key := redisconst.VidPageViewListKey(recentMinute.Unix())
|
||
vidStrs := make([]string, len(vids))
|
||
for i := range vids {
|
||
vidStrs[i] = vids[i].Hex()
|
||
}
|
||
if err := appg.Redis.RPush(key, vidStrs); err != nil {
|
||
return
|
||
}
|
||
_, _ = appg.Redis.ExpireKey(key, time.Minute*20)
|
||
}
|
||
|
||
func incTagCount(v tagmod.Tag, isValidPlay bool) error {
|
||
// 增加总播放量
|
||
tPlayCount := v.TPlayCount + 1
|
||
// 增加有效播放量量&更新播放率
|
||
vPlayCount := v.VPlayCount
|
||
var playRating float64
|
||
if isValidPlay {
|
||
vPlayCount++
|
||
playRating = maths.Decimal6Bit(float64(vPlayCount) / float64(tPlayCount))
|
||
} else {
|
||
playRating = maths.Decimal6Bit(float64(vPlayCount) / float64(tPlayCount))
|
||
}
|
||
doc := tagmod.TagUpdateDoc{
|
||
VPlayCount: &vPlayCount,
|
||
TPlayCount: &tPlayCount,
|
||
PlayRating: &playRating,
|
||
UpdatedAt: time.Now(),
|
||
}
|
||
if err := tagmod.UpdateOneTagByID(v.ID, doc); err != nil {
|
||
log.Error("incTagCount error", log.Any("tid", v.ID), log.Any("isValidPlay", isValidPlay), log.E(err))
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// IncreaseTag 播放视频对标签计数
|
||
func IncreaseTag(vidID string, tid primitive.ObjectID, isValidPlay bool) error {
|
||
if !tid.IsZero() {
|
||
tag, err := tagmod.FindOneTagByID(tid)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
_ = incTagCount(tag, isValidPlay)
|
||
return err
|
||
}
|
||
tags, err := vidmod.GetVideoTag(vidID)
|
||
if err != nil || len(tags) <= 0 {
|
||
log.Error("IncreaseTag error", log.Any("vidID", vidID), log.Any("isValidPlay", isValidPlay), log.E(err))
|
||
return err
|
||
}
|
||
tagList, err := tagmod.FindTagsByIDS(tags)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
for _, v := range tagList {
|
||
_ = incTagCount(v, isValidPlay)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// GetVideosByIDs 通过id获取视频列表信息
|
||
func GetVideosByIDs(uid uint64, ids []primitive.ObjectID) []*vidmod.VideoInfo {
|
||
if len(ids) == 0 {
|
||
log.Warn("GetVideosByIDs error", log.Any("uid", uid), log.Any("ids", ids))
|
||
return nil
|
||
}
|
||
vInfo, unExistsVid, err := getVideoListByIDsFromRedis(ids)
|
||
if err != nil || len(unExistsVid) != 0 {
|
||
unExistsVInfo, err := getVideoListByIDsFromMongo(unExistsVid)
|
||
if err != nil {
|
||
log.Error("GetVideosByIDs getVideoListByIDsFromMongo", log.Any("udi", uid), log.Any("ids", ids),
|
||
log.E(err))
|
||
return nil
|
||
}
|
||
common.Go(func() {
|
||
if cacheErr := setVideoListByIDs2Redis(unExistsVInfo); cacheErr != nil {
|
||
log.Error("GetVideosByIDs setVideoListByIDs2Redis", log.Any("unExistsVid", unExistsVid), log.Any("uid", uid),
|
||
log.E(cacheErr))
|
||
}
|
||
})
|
||
vInfo = append(vInfo, unExistsVInfo...)
|
||
}
|
||
if uid == 0 { // 用户未登录
|
||
return EncodeVideoInfoNoUID(vInfo)
|
||
}
|
||
return EncodeVideoInfo(uid, vInfo)
|
||
}
|
||
|
||
// GetVideosByIDsNoCache 通过id直接从MongoDB获取视频列表信息,不读写Redis缓存。
|
||
// 按传入ID顺序返回,保证收藏列表维持收藏时间倒序。
|
||
func GetVideosByIDsNoCache(uid uint64, ids []primitive.ObjectID) []*vidmod.VideoInfo {
|
||
if len(ids) == 0 {
|
||
return nil
|
||
}
|
||
vInfo, err := getVideoListByIDsFromMongo(ids)
|
||
if err != nil {
|
||
log.Error("GetVideosByIDsNoCache getVideoListByIDsFromMongo", log.Any("uid", uid), log.Any("ids", ids),
|
||
log.E(err))
|
||
return nil
|
||
}
|
||
vInfo = orderVideoModelsByIDs(ids, vInfo)
|
||
if uid == 0 {
|
||
return EncodeVideoInfoNoUID(vInfo)
|
||
}
|
||
return EncodeVideoInfo(uid, vInfo)
|
||
}
|
||
|
||
func orderVideoModelsByIDs(ids []primitive.ObjectID, videos []*vidmod.VideoModel) []*vidmod.VideoModel {
|
||
videoByID := make(map[primitive.ObjectID]*vidmod.VideoModel, len(videos))
|
||
for _, video := range videos {
|
||
if video != nil {
|
||
videoByID[video.ID] = video
|
||
}
|
||
}
|
||
ordered := make([]*vidmod.VideoModel, 0, len(videos))
|
||
for _, id := range ids {
|
||
if video, ok := videoByID[id]; ok {
|
||
ordered = append(ordered, video)
|
||
}
|
||
}
|
||
return ordered
|
||
}
|
||
|
||
// NewGetVideosByIDs 通过id获取视频列表信息
|
||
func NewGetVideosByIDs(uid uint64, ids []primitive.ObjectID) []*vidmod.VideoInfoResp {
|
||
if len(ids) == 0 {
|
||
log.Warn("GetVideosByIDs error", log.Any("uid", uid), log.Any("ids", ids))
|
||
return nil
|
||
}
|
||
vInfo, unExistsVid, err := getVideoListByIDsFromRedis(ids)
|
||
if err != nil || len(unExistsVid) != 0 {
|
||
unExistsVInfo, err := getVideoListByIDsFromMongo(unExistsVid)
|
||
if err != nil {
|
||
log.Error("GetVideosByIDs getVideoListByIDsFromMongo", log.Any("udi", uid), log.Any("ids", ids),
|
||
log.E(err))
|
||
return nil
|
||
}
|
||
|
||
common.Go(func() {
|
||
if cacheErr := setVideoListByIDs2Redis(unExistsVInfo); cacheErr != nil {
|
||
log.Error("GetVideosByIDs setVideoListByIDs2Redis", log.Any("unExistsVid", unExistsVid), log.Any("uid", uid),
|
||
log.E(cacheErr))
|
||
}
|
||
})
|
||
vInfo = append(vInfo, unExistsVInfo...)
|
||
// 将redis中获取不到的写入切片中后得按照原来id顺序的重新排序
|
||
videoMap := make(map[primitive.ObjectID]*vidmod.VideoModel)
|
||
for _, v := range vInfo {
|
||
videoMap[v.ID] = v
|
||
}
|
||
videoList := []*vidmod.VideoModel{}
|
||
for _, id := range ids {
|
||
video, ok := videoMap[id]
|
||
if !ok {
|
||
continue
|
||
}
|
||
videoList = append(videoList, video)
|
||
}
|
||
vInfo = videoList
|
||
}
|
||
//if uid == 0 { // 用户未登录
|
||
// return EncodeVideoInfoNoUID(vInfo)
|
||
//}
|
||
return NewEncodeVideoInfo(uid, vInfo)
|
||
}
|
||
|
||
func getVideoModelByIDs(ids []primitive.ObjectID) []*vidmod.VideoModel {
|
||
if len(ids) == 0 {
|
||
log.Warn("getVideoModelByIDs error", log.Any("ids", ids))
|
||
return nil
|
||
}
|
||
vInfo, unExistsVid, err := getVideoListByIDsFromRedis(ids)
|
||
if err != nil || len(unExistsVid) != 0 {
|
||
unExistsVInfo, err := getVideoListByIDsFromMongoBaseNoStatus(unExistsVid)
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
common.Go(func() {
|
||
_ = setVideoListByIDs2Redis(unExistsVInfo)
|
||
})
|
||
vInfo = append(vInfo, unExistsVInfo...)
|
||
}
|
||
return vInfo
|
||
}
|
||
|
||
// GetVideoListByIDsNoStatus 通过id获取视频列表信息 底层不判断视频状态, 由上次业务保证ID的正确性
|
||
func GetVideoListByIDsNoStatus(uid uint64, ids []primitive.ObjectID) []*vidmod.VideoInfoResp {
|
||
vInfo := getVideoModelByIDs(ids)
|
||
return NewEncodeVideoInfo(uid, vInfo)
|
||
}
|
||
|
||
func getRedis() *redis.Client {
|
||
if appg.Redis != nil {
|
||
return appg.Redis
|
||
}
|
||
if skdg.Redis != nil {
|
||
return skdg.Redis
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func setCache(recentMinute time.Time) {
|
||
common.Go(func() {
|
||
ids := getNewsTop200FromMongo(recentMinute)
|
||
if len(ids) != 0 {
|
||
setNewestNews2Redis(ids, recentMinute)
|
||
}
|
||
})
|
||
}
|
||
|
||
func setCache_old() {
|
||
common.Go(func() {
|
||
ids := getNewsTop200FromMongo_old()
|
||
if len(ids) != 0 {
|
||
setNewestNews2Redis_old(ids)
|
||
}
|
||
})
|
||
}
|
||
|
||
func setShortVideoCache() {
|
||
common.Go(func() {
|
||
ids := getNewsShortVideoTop200FromMongo()
|
||
if len(ids) != 0 {
|
||
setNewestShortVideo2Redis(ids)
|
||
}
|
||
})
|
||
}
|
||
|
||
// GetNewestNews 获取最新帖子
|
||
// 最新两百条存redis,其他数据库查找
|
||
func GetNewestNews(page, size uint64, reqTime time.Time) ([]*vidmod.VideoModel, bool, error) {
|
||
recentMinute := timerange.RecentMinute(reqTime, statrecordmod.FiveMinuteScale)
|
||
endpoint := page * size
|
||
if endpoint > MaxNewestCacheNum { //大于200条的数据库获取
|
||
return getNewestNewsFromMongo(page, size, recentMinute)
|
||
}
|
||
ids, err := getNewestNewsFromRedis(page, size, recentMinute)
|
||
if err != nil || uint64(len(ids)) < size { //缓存获取失败,数据库获取
|
||
infos, hasNext, err := getNewestNewsFromMongo(page, size, recentMinute)
|
||
setCache(recentMinute)
|
||
return infos, hasNext, err
|
||
}
|
||
oids := common.String2ObjectID(ids)
|
||
infos := getVideoModelByIDs(oids)
|
||
if len(infos) == 0 {
|
||
log.Warn("info length error", log.Any("page", page), log.Any("size", size), log.Any("reqTime", reqTime), log.Any("recentMinute", recentMinute), log.Any("ids", ids))
|
||
}
|
||
return infos, true, nil
|
||
}
|
||
|
||
// GetNewestNews 获取最新帖子
|
||
// 最新两百条存redis,其他数据库查找
|
||
func GetNewestNews_old(page, size uint64) ([]*vidmod.VideoModel, bool, error) {
|
||
if endpoint := page * size; endpoint > MaxNewestCacheNum { //大于200条的数据库获取
|
||
return getNewestNewsFromMongo_old(page, size)
|
||
}
|
||
ids, err := getNewestNewsFromRedis_old(page, size)
|
||
if err != nil || uint64(len(ids)) < size { //缓存获取失败,数据库获取
|
||
infos, hasNext, err := getNewestNewsFromMongo_old(page, size)
|
||
setCache_old()
|
||
return infos, hasNext, err
|
||
}
|
||
oids := common.String2ObjectID(ids)
|
||
infos := getVideoModelByIDs(oids)
|
||
if len(infos) == 0 {
|
||
log.Warn("info length error_old", log.Any("page", page), log.Any("size", size), log.Any("ids", ids))
|
||
}
|
||
return infos, true, nil
|
||
}
|
||
|
||
func GetNewestShortVideo(page, size uint64) ([]*vidmod.VideoModel, bool, error) {
|
||
if endpoint := page * size; endpoint > MaxNewestCacheNum { //大于200条的数据库获取
|
||
return getNewestShortVideoFromMongo(page, size)
|
||
}
|
||
ids, err := getNewestShortVideoFromRedis(page, size)
|
||
if err != nil || uint64(len(ids)) < size { //缓存获取失败,数据库获取
|
||
infos, hasNext, err := getNewestShortVideoFromMongo(page, size)
|
||
setShortVideoCache()
|
||
return infos, hasNext, err
|
||
}
|
||
oids := common.String2ObjectID(ids)
|
||
infos := getVideoModelByIDs(oids)
|
||
if len(infos) == 0 {
|
||
log.Warn("info length error_old", log.Any("page", page), log.Any("size", size), log.Any("ids", ids))
|
||
}
|
||
return infos, true, nil
|
||
}
|
||
|
||
// IncCommentCount 评论统计次数加加
|
||
func IncCommentCount(id primitive.ObjectID) error {
|
||
if err := vidmod.IncCommentCount(id); err != nil {
|
||
return err
|
||
}
|
||
incComment(id)
|
||
return nil
|
||
}
|
||
|
||
// 修正用户喜欢的视频记录
|
||
func FixLikeVideoRecord(uid uint64, oids []primitive.ObjectID) {
|
||
back, err := vidmod.GetVideoListByIDsUnPublish(oids)
|
||
if err != nil || len(back) == 0 {
|
||
return
|
||
}
|
||
vids := make([]primitive.ObjectID, len(back))
|
||
for i, v := range back {
|
||
vids[i] = v.ID
|
||
}
|
||
}
|
||
|
||
// NewTransfer2Info 将视频数据库模式转为返回模式
|
||
func NewTransfer2Info(v *vidmod.VideoModel, uInfo vidmod.Publisher, vStatus vidmod.VideoStatus, tags []vidmod.TagInfo, originCoins int64) *vidmod.VideoInfoResp {
|
||
if v.Status == 3 {
|
||
v.Status = 1
|
||
}
|
||
newType := vidmod.SP
|
||
if len(v.NewsType) != 0 {
|
||
newType = v.NewsType
|
||
}
|
||
base := vidmod.VideoInfoResp{
|
||
ID: v.ID,
|
||
NewsType: newType,
|
||
Title: v.Title,
|
||
PlayTime: v.PlayTime,
|
||
Cover: v.Cover,
|
||
SeriesCover: v.SeriesCover,
|
||
PlayCount: (v.PlayCount + v.FakePlayCount) * VideoPlayCountMultiplier,
|
||
LikeCount: v.LikeCount, // 移除like数量加倍,修复各处like数不一致问题
|
||
PageViewCount: v.PageViewCount * 3,
|
||
CommentCount: v.CommentCount,
|
||
Coins: v.Coins,
|
||
Size: v.Size,
|
||
Resolution: v.Resolution,
|
||
Ratio: v.Ratio,
|
||
CreatedAt: v.CreatedAt,
|
||
ReviewAt: v.ReviewAt,
|
||
FreeTime: v.FreeTime,
|
||
Status: v.Status,
|
||
Reason: v.Reason,
|
||
SourceURL: v.SourceURL,
|
||
H265Url: H265URLForApp(v),
|
||
LinkUrl: v.LinkUrl,
|
||
//IsTopping: v.IsTopping,
|
||
IsChoosen: v.IsChoosen,
|
||
IsRecommend: v.IsRecommend,
|
||
Rewarded: v.Rewarded.Add(v.FakeRewarded),
|
||
OriginCoins: originCoins,
|
||
Chosen: v.Chosen,
|
||
DownloadAllow: v.DownloadAllow,
|
||
ShowType: v.ShowType,
|
||
FreeArea: v.FreeArea,
|
||
Tags: tags,
|
||
}
|
||
if v.LiaoBaTopSort > 0 {
|
||
base.IsTopping = true
|
||
}
|
||
var vipLevel int
|
||
if uInfo.VipExpireDate.After(time.Now()) {
|
||
vipLevel = uInfo.VipLevel
|
||
}
|
||
u := vidmod.AppPublisherResp{
|
||
UID: uInfo.UID,
|
||
Name: uInfo.Name,
|
||
Portrait: uInfo.Portrait,
|
||
HasFollowed: uInfo.HasFollowed,
|
||
VipLevel: vipLevel,
|
||
VipName: uInfo.VipName,
|
||
}
|
||
base.UInfo = u
|
||
base.VidStatus = vStatus
|
||
info := base
|
||
|
||
return &info
|
||
}
|
||
|
||
func ToVideoBaseInfo(v *vidmod.VideoModel, ti []vidmod.TagInfo, originCoins int64) *vidmod.VideoBase {
|
||
if v.Status == 3 {
|
||
v.Status = 1
|
||
}
|
||
newType := vidmod.SP
|
||
if len(v.NewsType) != 0 {
|
||
newType = v.NewsType
|
||
}
|
||
|
||
base := vidmod.VideoBase{
|
||
ID: v.ID,
|
||
NewsType: newType,
|
||
Title: v.Title,
|
||
Content: v.Content,
|
||
Tags: ti,
|
||
SourceURL: v.SourceURL,
|
||
H265Url: H265URLForApp(v),
|
||
PreviewURL: v.PreviewURL,
|
||
LinkUrl: v.LinkUrl,
|
||
PlayTime: v.PlayTime,
|
||
Cover: v.Cover,
|
||
CoverThumb: v.CoverThumb,
|
||
SeriesCover: v.SeriesCover,
|
||
LikeCount: v.LikeCount, // 移除like数量加倍,修复各处like数不一致问题
|
||
PlayCount: (v.PlayCount + v.FakePlayCount) * VideoPlayCountMultiplier,
|
||
PurchaseCount: v.PurchaseCount,
|
||
CommentCount: v.CommentCount,
|
||
ShareCount: v.FakeShareCount,
|
||
Coins: v.Coins,
|
||
Size: v.Size,
|
||
Resolution: v.Resolution,
|
||
Ratio: v.Ratio,
|
||
Status: v.Status,
|
||
Reason: v.Reason,
|
||
FreeTime: v.FreeTime,
|
||
IsHideLocation: v.IsHideLocation,
|
||
FreeArea: v.FreeArea,
|
||
CreatedAt: v.CreatedAt,
|
||
ReviewAt: v.ReviewAt,
|
||
IsTopping: v.IsTopping,
|
||
IsRecommend: v.IsRecommend,
|
||
IsChoosen: v.IsChoosen,
|
||
Rewarded: v.Rewarded.Add(v.FakeRewarded),
|
||
OriginCoins: originCoins,
|
||
CollectCount: v.CollectCount,
|
||
PageViewCount: v.PageViewCount * 3,
|
||
SeedLinkUrl: v.SeedLinkUrl,
|
||
SeedSize: v.SeedSize,
|
||
SeedPlayTime: v.SeedPlayTime,
|
||
PreviewStart: v.PreviewStart,
|
||
RichText: v.RichText,
|
||
TimeNodeList: v.TimeNodeList,
|
||
DownloadAllow: v.DownloadAllow,
|
||
ShowType: v.ShowType,
|
||
}
|
||
return &base
|
||
}
|