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 }