Initial commit

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 13:57:10 +08:00
co-authored by Claude Opus 5
commit 8679200f41
1897 changed files with 257900 additions and 0 deletions
+120
View File
@@ -0,0 +1,120 @@
package vidhelpser
import (
"time"
"91porn-server/app/service/sys_config"
"91porn-server/models/cache/sysconfdata"
"91porn-server/models/v/sysconfmod"
"91porn-server/models/v/usermod"
"91porn-server/models/v/vidmod"
)
// FreeTrialBadgeContext 是一次视频列表组装过程中复用的用户免费观看上下文。
type FreeTrialBadgeContext struct {
uid uint64
enabled bool
remaining uint64
canShow bool
canUse bool
}
// NewFreeTrialBadgeContext 构造免费观看上下文。
// remaining 会按系统配置的总次数截断,避免历史异常数据透传给前端。
func NewFreeTrialBadgeContext(uid, total, remaining uint64, isVIP, enabled bool) FreeTrialBadgeContext {
if total == 0 || uid == 0 {
remaining = 0
} else if remaining > total {
remaining = total
}
canShow := uid > 0 && total > 0 && !isVIP
return FreeTrialBadgeContext{
uid: uid,
enabled: enabled,
remaining: remaining,
canShow: canShow,
canUse: canShow && remaining > 0,
}
}
// LoadFreeTrialBadgeContext 加载用户和后台角标开关。
func LoadFreeTrialBadgeContext(uid uint64) FreeTrialBadgeContext {
if uid == 0 {
return FreeTrialBadgeContext{}
}
user, err := usermod.FindUserByUID(uid)
if err != nil || user == nil {
return FreeTrialBadgeContext{}
}
return FreeTrialBadgeContextForUser(uid, user)
}
// FreeTrialBadgeContextForUser 使用已查询的用户构造上下文,避免列表组装重复查询用户。
func FreeTrialBadgeContextForUser(uid uint64, user *usermod.User) FreeTrialBadgeContext {
if uid == 0 || user == nil {
return FreeTrialBadgeContext{}
}
enabled := false
if value, err := sysconfdata.GetBoolFromSharedCache(sysconfmod.VCodeFreeTrialBadgeEnabled); err == nil {
enabled = value
}
return NewFreeTrialBadgeContext(
uid,
sys_config.GetTotalWatchCount(),
user.WatchCount,
user.IsVIP(time.Now()),
enabled,
)
}
// Fields 返回当前视频的角标字段。
func (c FreeTrialBadgeContext) Fields(
newsType string,
originCoins int64,
freeArea bool,
publisherID uint64,
) (show bool, remaining uint64, canUse bool) {
remaining = c.remaining
eligible := isVIPVideoForFreeTrial(newsType, originCoins, freeArea) &&
publisherID != c.uid
canUse = c.canUse && eligible
show = c.enabled && canUse
return
}
func isVIPVideoForFreeTrial(newsType string, originCoins int64, freeArea bool) bool {
if originCoins != 0 || freeArea {
return false
}
return newsType == vidmod.SP || newsType == vidmod.SHORT
}
// ApplyFreeTrialBadgeToVideoInfos 设置标准视频列表/详情对象的角标字段。
func ApplyFreeTrialBadgeToVideoInfos(ctx FreeTrialBadgeContext, videos []*vidmod.VideoInfo) {
for _, video := range videos {
if video == nil {
continue
}
video.ShowFreeTrialBadge, video.FreeTrialRemaining, video.CanUseFreeTrial = ctx.Fields(
video.NewsType,
video.OriginCoins,
video.FreeArea,
video.UInfo.UID,
)
}
}
// ApplyFreeTrialBadgeToVideoInfoResps 设置无状态视频列表对象的角标字段。
func ApplyFreeTrialBadgeToVideoInfoResps(ctx FreeTrialBadgeContext, videos []*vidmod.VideoInfoResp) {
for _, video := range videos {
if video == nil {
continue
}
video.ShowFreeTrialBadge, video.FreeTrialRemaining, video.CanUseFreeTrial = ctx.Fields(
video.NewsType,
video.OriginCoins,
video.FreeArea,
video.UInfo.UID,
)
}
}
+134
View File
@@ -0,0 +1,134 @@
package vidhelpser
import (
"testing"
"91porn-server/models/v/vidmod"
)
func TestFreeTrialBadgeContextFields(t *testing.T) {
const (
uid = uint64(1001)
publisherID = uint64(2002)
)
tests := []struct {
name string
ctx FreeTrialBadgeContext
newsType string
originCoins int64
freeArea bool
publisherID uint64
wantShow bool
wantRemaining uint64
wantCanUse bool
}{
{
name: "eligible vip video",
ctx: NewFreeTrialBadgeContext(uid, 3, 2, false, true),
newsType: vidmod.SP,
publisherID: publisherID,
wantShow: true,
wantRemaining: 2,
wantCanUse: true,
},
{
name: "switch only hides badge",
ctx: NewFreeTrialBadgeContext(uid, 3, 2, false, false),
newsType: vidmod.SHORT,
publisherID: publisherID,
wantRemaining: 2,
wantCanUse: true,
},
{
name: "active vip does not use trial",
ctx: NewFreeTrialBadgeContext(uid, 3, 2, true, true),
newsType: vidmod.SP,
publisherID: publisherID,
wantRemaining: 2,
},
{
name: "coin video",
ctx: NewFreeTrialBadgeContext(uid, 3, 2, false, true),
newsType: vidmod.SP,
originCoins: 10,
publisherID: publisherID,
wantRemaining: 2,
},
{
name: "free area",
ctx: NewFreeTrialBadgeContext(uid, 3, 2, false, true),
newsType: vidmod.SP,
freeArea: true,
publisherID: publisherID,
wantRemaining: 2,
},
{
name: "own video",
ctx: NewFreeTrialBadgeContext(uid, 3, 2, false, true),
newsType: vidmod.SP,
publisherID: uid,
wantRemaining: 2,
},
{
name: "advertisement",
ctx: NewFreeTrialBadgeContext(uid, 3, 2, false, true),
newsType: vidmod.AD_SP,
publisherID: publisherID,
wantRemaining: 2,
},
{
name: "remaining count is clamped",
ctx: NewFreeTrialBadgeContext(uid, 3, 99, false, true),
newsType: vidmod.SP,
publisherID: publisherID,
wantShow: true,
wantRemaining: 3,
wantCanUse: true,
},
{
name: "no remaining count hides badge",
ctx: NewFreeTrialBadgeContext(uid, 3, 0, false, true),
newsType: vidmod.SP,
publisherID: publisherID,
wantRemaining: 0,
wantCanUse: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
show, remaining, canUse := tt.ctx.Fields(tt.newsType, tt.originCoins, tt.freeArea, tt.publisherID)
if show != tt.wantShow || remaining != tt.wantRemaining || canUse != tt.wantCanUse {
t.Fatalf(
"Fields() = (%v, %d, %v), want (%v, %d, %v)",
show,
remaining,
canUse,
tt.wantShow,
tt.wantRemaining,
tt.wantCanUse,
)
}
})
}
}
func TestApplyFreeTrialBadgeToVideoInfos(t *testing.T) {
ctx := NewFreeTrialBadgeContext(1001, 3, 2, false, true)
video := &vidmod.VideoInfo{
VideoBase: vidmod.VideoBase{
NewsType: vidmod.SP,
OriginCoins: 0,
},
UInfo: vidmod.Publisher{
UInfo: vidmod.UInfo{UID: 2002},
},
}
ApplyFreeTrialBadgeToVideoInfos(ctx, []*vidmod.VideoInfo{video})
if !video.ShowFreeTrialBadge || !video.CanUseFreeTrial || video.FreeTrialRemaining != 2 {
t.Fatalf("unexpected badge fields: %+v", video.VideoBase)
}
}
+15
View File
@@ -0,0 +1,15 @@
package vidhelpser
import (
"91porn-server/app/appg"
"91porn-server/models/v/vidmod"
)
// H265URLForApp 根据 App 配置决定是否下发已经入库的 H265 播放地址。
// enableApp 未配置时保持向后兼容;显式配置为 false 时可快速关闭 H265 下发。
func H265URLForApp(video *vidmod.VideoModel) string {
if video == nil {
return ""
}
return appg.H265URLForApp(video.H265Url)
}
+119
View File
@@ -0,0 +1,119 @@
package vidhelpser
import (
"encoding/json"
"testing"
"91porn-server/app/appg"
"91porn-server/models/v/locmod"
"91porn-server/models/v/vidmod"
)
func TestH265URLForApp(t *testing.T) {
oldConf := appg.Conf
t.Cleanup(func() {
appg.Conf = oldConf
})
video := &vidmod.VideoModel{H265Url: "https://cdn.example.com/h265/index.m3u8"}
if got := H265URLForApp(nil); got != "" {
t.Fatalf("nil video returned %q", got)
}
appg.Conf = nil
if got := H265URLForApp(video); got != video.H265Url {
t.Fatalf("unconfigured switch returned %q", got)
}
disabled := false
appg.Conf = &appg.GlobalConfig{}
appg.Conf.Hevc.EnableApp = &disabled
if got := H265URLForApp(video); got != "" {
t.Fatalf("disabled switch returned %q", got)
}
enabled := true
appg.Conf.Hevc.EnableApp = &enabled
if got := H265URLForApp(video); got != video.H265Url {
t.Fatalf("enabled switch returned %q", got)
}
}
func TestTransfer2InfoIncludesH265URL(t *testing.T) {
video := &vidmod.VideoModel{
SourceURL: "https://cdn.example.com/h264/index.m3u8",
H265Url: "https://cdn.example.com/h265/index.m3u8",
}
info := transfer2Info(
video,
vidmod.Publisher{},
vidmod.VideoStatus{},
locmod.Location{},
nil,
vidmod.CommentInfo{},
0,
)
data, err := json.Marshal(info)
if err != nil {
t.Fatalf("marshal video info: %v", err)
}
var response map[string]interface{}
if err := json.Unmarshal(data, &response); err != nil {
t.Fatalf("unmarshal video info response: %v", err)
}
if response["sourceURL"] != video.SourceURL {
t.Fatalf("unexpected H.264 URL: %v", response["sourceURL"])
}
if response["h265Url"] != video.H265Url {
t.Fatalf("unexpected H.265 URL: %v", response["h265Url"])
}
}
func TestNewTransfer2InfoIncludesH265URL(t *testing.T) {
video := &vidmod.VideoModel{
SourceURL: "https://cdn.example.com/h264/index.m3u8",
H265Url: "https://cdn.example.com/h265/index.m3u8",
}
info := NewTransfer2Info(
video,
vidmod.Publisher{},
vidmod.VideoStatus{},
nil,
0,
)
data, err := json.Marshal(info)
if err != nil {
t.Fatalf("marshal video info response: %v", err)
}
var response map[string]interface{}
if err := json.Unmarshal(data, &response); err != nil {
t.Fatalf("unmarshal video info response: %v", err)
}
if response["sourceURL"] != video.SourceURL {
t.Fatalf("unexpected H.264 URL: %v", response["sourceURL"])
}
if response["h265Url"] != video.H265Url {
t.Fatalf("unexpected H.265 URL: %v", response["h265Url"])
}
}
func TestToVideoBaseInfoIncludesH265URL(t *testing.T) {
video := &vidmod.VideoModel{
SourceURL: "https://cdn.example.com/h264/index.m3u8",
H265Url: "https://cdn.example.com/h265/index.m3u8",
}
info := ToVideoBaseInfo(video, nil, 0)
if info.SourceURL != video.SourceURL {
t.Fatalf("unexpected H.264 URL: %q", info.SourceURL)
}
if info.H265Url != video.H265Url {
t.Fatalf("unexpected H.265 URL: %q", info.H265Url)
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,114 @@
package vidhelpser
import (
"go/ast"
"go/parser"
"go/token"
"path/filepath"
"runtime"
"testing"
)
func parseHelpserSource(t *testing.T) *ast.File {
t.Helper()
_, testFile, _, ok := runtime.Caller(0)
if !ok {
t.Fatal("resolve test source path")
}
path := filepath.Join(filepath.Dir(testFile), "helpser.go")
file, err := parser.ParseFile(token.NewFileSet(), path, nil, 0)
if err != nil {
t.Fatalf("parse %s: %v", path, err)
}
return file
}
func findFunction(t *testing.T, file *ast.File, name string) *ast.FuncDecl {
t.Helper()
for _, decl := range file.Decls {
fn, ok := decl.(*ast.FuncDecl)
if ok && fn.Name.Name == name {
return fn
}
}
t.Fatalf("function %s not found", name)
return nil
}
func TestVideoInfoEncodersAggregatePageViewsOnlyThroughRedis(t *testing.T) {
file := parseHelpserSource(t)
encoders := []string{
"encodeVideoInfo",
"newEncodeVideoInfoNotStatus",
"newEncodeVideoInfo",
"encodeVideoInfoNoUID",
}
for _, name := range encoders {
t.Run(name, func(t *testing.T) {
fn := findFunction(t, file, name)
directMongoCalls := 0
redisAggregateCalls := 0
ast.Inspect(fn.Body, func(node ast.Node) bool {
call, ok := node.(*ast.CallExpr)
if !ok {
return true
}
switch callee := call.Fun.(type) {
case *ast.Ident:
if callee.Name == "incVideoPageView" {
redisAggregateCalls++
}
case *ast.SelectorExpr:
pkg, ok := callee.X.(*ast.Ident)
if ok && pkg.Name == "vidmod" && callee.Sel.Name == "IncVideoPageView" {
directMongoCalls++
}
}
return true
})
if directMongoCalls != 0 {
t.Fatalf("direct Mongo page-view increments = %d, want 0", directMongoCalls)
}
if redisAggregateCalls != 1 {
t.Fatalf("Redis page-view aggregate calls = %d, want 1", redisAggregateCalls)
}
})
}
}
func TestAsyncClosuresDoNotAssignCapturedErr(t *testing.T) {
file := parseHelpserSource(t)
functions := []string{
"encodeVideoInfo",
"GetVideosByIDs",
"NewGetVideosByIDs",
}
for _, name := range functions {
t.Run(name, func(t *testing.T) {
fn := findFunction(t, file, name)
ast.Inspect(fn.Body, func(node ast.Node) bool {
closure, ok := node.(*ast.FuncLit)
if !ok {
return true
}
ast.Inspect(closure.Body, func(node ast.Node) bool {
assign, ok := node.(*ast.AssignStmt)
if !ok || assign.Tok != token.ASSIGN {
return true
}
for _, lhs := range assign.Lhs {
ident, ok := lhs.(*ast.Ident)
if ok && ident.Name == "err" {
t.Errorf("async closure assigns captured err at %s", name)
}
}
return true
})
return false
})
})
}
}
+88
View File
@@ -0,0 +1,88 @@
package vidhelpser
import (
"time"
"91porn-server/models/v/tagmod"
"91porn-server/models/v/usermod"
"91porn-server/models/v/vidmod"
"go.mongodb.org/mongo-driver/bson/primitive"
)
func getUsersBaseInfoFromMongo(uids []uint64) ([]*usermod.BaseInfoVip, error) {
return usermod.GetUsersBaseInfoWithVip(uids)
}
func getTagsByIDsFromMongo(tids []primitive.ObjectID) ([]vidmod.TagInfo, error) {
tInfo, err := tagmod.FindTagsByIDS(tids)
if err != nil {
return []vidmod.TagInfo{}, err
}
info := make([]vidmod.TagInfo, len(tInfo))
for i, v := range tInfo {
info[i] = vidmod.TagInfo{
ID: v.ID,
Name: v.TagName,
CoverImg: v.CoverImg,
Description: v.Description,
}
}
return info, nil
}
func getVideoListByIDsFromMongo(vids []primitive.ObjectID) ([]*vidmod.VideoModel, error) {
return vidmod.GetVideoListByIDs(vids)
}
func getVideoListByIDsFromMongoBaseNoStatus(vids []primitive.ObjectID) ([]*vidmod.VideoModel, error) {
return vidmod.GetVideoListByIDsNoStatus(vids)
}
func getNewestNewsFromMongo(page, size uint64, recentMinute time.Time) ([]*vidmod.VideoModel, bool, error) {
return vidmod.GetNewestNews(page, size, recentMinute)
}
func getNewestNewsFromMongo_old(page, size uint64) ([]*vidmod.VideoModel, bool, error) {
return vidmod.GetNewestNews_old(page, size)
}
func getNewestShortVideoFromMongo(page, size uint64) ([]*vidmod.VideoModel, bool, error) {
return vidmod.GetNewestShortVideo(page, size)
}
func getNewsTop200FromMongo(recentMinute time.Time) []string {
vModel, _, err := vidmod.GetNewestNews(1, MaxNewestCacheNum, recentMinute)
if err != nil {
return []string{}
}
ids := make([]string, len(vModel))
for i, v := range vModel {
ids[i] = v.ID.Hex()
}
return ids
}
func getNewsTop200FromMongo_old() []string {
vModel, _, err := vidmod.GetNewestNews_old(1, MaxNewestCacheNum)
if err != nil {
return []string{}
}
ids := make([]string, len(vModel))
for i, v := range vModel {
ids[i] = v.ID.Hex()
}
return ids
}
func getNewsShortVideoTop200FromMongo() []string {
vModel, _, err := vidmod.GetNewestShortVideo(1, MaxNewestCacheNum)
if err != nil {
return []string{}
}
ids := make([]string, len(vModel))
for i, v := range vModel {
ids[i] = v.ID.Hex()
}
return ids
}
+317
View File
@@ -0,0 +1,317 @@
package vidhelpser
import (
"encoding/json"
"fmt"
"time"
"91porn-server/app/appg"
"91porn-server/common/constant/redisconst"
"91porn-server/common/log"
"91porn-server/common/redis"
"91porn-server/models/v/usermod"
"91porn-server/models/v/vidmod"
"go.mongodb.org/mongo-driver/bson/primitive"
)
func getUIDKey(uids []uint64) []string {
format := redisconst.UserInfoKey()
keys := make([]string, len(uids))
for i, u := range uids {
keys[i] = fmt.Sprintf(format, u)
}
return keys
}
func getTidKey(tids []primitive.ObjectID) []string {
format := redisconst.TagInfoKey()
keys := make([]string, len(tids))
for i, t := range tids {
keys[i] = fmt.Sprintf(format, t.Hex())
}
return keys
}
func getVidKey(vids []primitive.ObjectID) []string {
format := redisconst.VideoInfoKey()
keys := make([]string, len(vids))
for i, v := range vids {
keys[i] = fmt.Sprintf(format, v.Hex())
}
return keys
}
func parseUserInfo(data []interface{}) []usermod.BaseInfoVip {
infos := make([]usermod.BaseInfoVip, 0, len(data))
for _, d := range data {
str, ok := d.(string)
if !ok {
continue
}
var u usermod.BaseInfoVip
if err := json.Unmarshal([]byte(str), &u); err != nil {
continue
}
infos = append(infos, u)
}
return infos
}
func getPublisher(info []usermod.BaseInfoVip) map[uint64]*vidmod.Publisher {
m := make(map[uint64]*vidmod.Publisher)
for _, u := range info {
p := vidmod.Publisher{
UInfo: u,
}
m[u.UID] = &p
}
return m
}
func getUnExistUID(uids []uint64, m map[uint64]*vidmod.Publisher) []uint64 {
ids := make([]uint64, 0, len(uids))
for _, u := range uids {
if m[u] == nil {
ids = append(ids, u)
}
}
return ids
}
func getUsersBaseInfoFromRedis(uids []uint64) (map[uint64]*vidmod.Publisher, []uint64, error) {
sInfo := getUIDKey(uids)
uInfo, err := appg.Redis.MGet(sInfo...)
if err != nil {
log.Error("getUsersBaseInfoFromRedis error", log.Any("uids", uids), log.Any("sInfo", sInfo), log.E(err))
return nil, uids, err
}
userInfo := parseUserInfo(uInfo)
mPublisher := getPublisher(userInfo)
unExist := getUnExistUID(uids, mPublisher)
return mPublisher, unExist, nil
}
func setUsersBaseInfo2Redis(infos []*usermod.BaseInfoVip) error {
format := redisconst.UserInfoKey()
expire := redisconst.UserInfoExpire()
for _, u := range infos {
d, err := json.Marshal(u)
if err != nil {
continue
}
s := fmt.Sprintf(format, u.UID)
_ = appg.Redis.Set(s, string(d), expire)
}
return nil
}
func parseTagInfo(data []interface{}) []vidmod.TagInfo {
infos := make([]vidmod.TagInfo, 0, len(data))
for _, d := range data {
str, ok := d.(string)
if !ok {
continue
}
var t vidmod.TagInfo
if err := json.Unmarshal([]byte(str), &t); err != nil {
continue
}
infos = append(infos, t)
}
return infos
}
func getUnExistTID(ids []primitive.ObjectID, info []vidmod.TagInfo) []primitive.ObjectID {
m := make(map[primitive.ObjectID]bool)
for _, i := range info {
m[i.ID] = true
}
unExist := make([]primitive.ObjectID, 0, len(ids))
for _, i := range ids {
if !m[i] {
unExist = append(unExist, i)
}
}
return unExist
}
func getTagsByIDsFromRedis(ids []primitive.ObjectID) ([]vidmod.TagInfo, []primitive.ObjectID, error) {
sInfo := getTidKey(ids)
uInfo, err := appg.Redis.MGet(sInfo...)
if err != nil {
log.Error("getTagsByIDsFromRedis error", log.Any("ids", ids), log.Any("sInfo", sInfo), log.E(err))
return nil, ids, err
}
tagInfo := parseTagInfo(uInfo)
unExist := getUnExistTID(ids, tagInfo)
return tagInfo, unExist, nil
}
func setTagsByIDs2Redis(tags []vidmod.TagInfo) error {
expire := redisconst.TagInfoExpire()
format := redisconst.TagInfoKey()
for _, t := range tags {
d, err := json.Marshal(t)
if err != nil {
continue
}
s := fmt.Sprintf(format, t.ID.Hex())
_ = appg.Redis.Set(s, string(d), expire)
}
return nil
}
func parseVideoInfo(data []interface{}) []*vidmod.VideoModel {
infos := make([]*vidmod.VideoModel, 0, len(data))
for _, d := range data {
str, ok := d.(string)
if !ok {
continue
}
var v vidmod.VideoModel
if err := json.Unmarshal([]byte(str), &v); err != nil {
continue
}
infos = append(infos, &v)
}
return infos
}
func getUnExistVID(ids []primitive.ObjectID, info []*vidmod.VideoModel) []primitive.ObjectID {
m := make(map[primitive.ObjectID]bool)
for _, i := range info {
m[i.ID] = true
}
unExist := make([]primitive.ObjectID, 0, len(ids))
for _, i := range ids {
if !m[i] {
unExist = append(unExist, i)
}
}
return unExist
}
func getVideoListByIDsFromRedis(ids []primitive.ObjectID) ([]*vidmod.VideoModel, []primitive.ObjectID, error) {
sInfo := getVidKey(ids)
uInfo, err := appg.Redis.MGet(sInfo...)
if err != nil {
log.Error("getVideoListByIDsFromRedis error", log.Any("ids", ids), log.Any("sInfo", sInfo), log.E(err))
return nil, ids, err
}
videoInfo := parseVideoInfo(uInfo)
unExist := getUnExistVID(ids, videoInfo)
return videoInfo, unExist, nil
}
func setVideoListByIDs2Redis(infos []*vidmod.VideoModel) error {
expire := redisconst.VideoInfoExpire()
format := redisconst.VideoInfoKey()
for _, v := range infos {
d, err := json.Marshal(v)
if err != nil {
continue
}
s := fmt.Sprintf(format, v.ID.Hex())
_ = appg.Redis.Set(s, string(d), expire)
}
return nil
}
func getNewestNewsFromRedis(page, size uint64, recentMinute time.Time) ([]string, error) {
key := redisconst.NewestNewsKey(recentMinute)
start := (page - 1) * size
end := page*size - 1
ids, err := appg.Redis.ZRange(key, int64(start), int64(end))
if err != nil {
log.Error("getNewestNewsFromRedis error", log.Any("page", page), log.Any("size", size), log.E(err))
}
return ids, err
}
func getNewestNewsFromRedis_old(page, size uint64) ([]string, error) {
key := redisconst.NewestNewsKey_old()
start := (page - 1) * size
end := page*size - 1
ids, err := appg.Redis.ZRange(key, int64(start), int64(end))
if err != nil {
log.Error("getNewestNewsFromRedis error", log.Any("page", page), log.Any("size", size), log.E(err))
}
return ids, err
}
func getNewestShortVideoFromRedis(page, size uint64) ([]string, error) {
key := redisconst.NewestShortVideoKey()
start := (page - 1) * size
end := page*size - 1
ids, err := appg.Redis.ZRange(key, int64(start), int64(end))
if err != nil {
log.Error("getNewestNewsFromRedis error", log.Any("page", page), log.Any("size", size), log.E(err))
}
return ids, err
}
func news2RedisZ(ids []string) []redis.Member {
if len(ids) == 0 {
return []redis.Member{}
}
members := make([]redis.Member, 0, len(ids))
for k, v := range ids {
if v != "" {
members = append(members, redis.Member{Score: float64(k), Member: v})
}
}
return members
}
func setNewestNews2Redis(ids []string, recentMinute time.Time) {
key := redisconst.NewestNewsKey(recentMinute)
expird := redisconst.NewestNewsExpire()
if _, err := appg.Redis.Del(key); err != nil {
return
}
values := news2RedisZ(ids)
if _, err := appg.Redis.ZAdd(key, values...); err != nil {
return
}
_, _ = appg.Redis.ExpireKeAt(key, time.Now().Add(expird))
}
func setNewestNews2Redis_old(ids []string) {
key := redisconst.NewestNewsKey_old()
expird := redisconst.NewestNewsExpire()
if _, err := appg.Redis.Del(key); err != nil {
return
}
values := news2RedisZ(ids)
if _, err := appg.Redis.ZAdd(key, values...); err != nil {
return
}
_, _ = appg.Redis.ExpireKeAt(key, time.Now().Add(expird))
}
func setNewestShortVideo2Redis(ids []string) {
key := redisconst.NewestShortVideoKey()
expird := redisconst.NewestNewsExpire()
if _, err := appg.Redis.Del(key); err != nil {
return
}
values := news2RedisZ(ids)
if _, err := appg.Redis.ZAdd(key, values...); err != nil {
return
}
_, _ = appg.Redis.ExpireKeAt(key, time.Now().Add(expird))
}
func incComment(vid primitive.ObjectID) {
infos, _, err := getVideoListByIDsFromRedis([]primitive.ObjectID{vid})
if err != nil || len(infos) == 0 {
return
}
if infos[0] == nil {
return
}
infos[0].CommentCount++
infos[0].FakeCommentCount++
_ = setVideoListByIDs2Redis(infos)
}