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
+89
View File
@@ -0,0 +1,89 @@
package vidhelp
import (
"encoding/json"
"fmt"
"91porn-server/common/constant/redisconst"
"91porn-server/common/log"
"91porn-server/models/v/vidmod"
"91porn-server/web/webg"
"go.mongodb.org/mongo-driver/bson/primitive"
)
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 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 getVideoListByIDsFromRedis(ids []primitive.ObjectID) ([]*vidmod.VideoModel, error) {
sInfo := getVidKey(ids)
uInfo, err := webg.Redis.MGet(sInfo...)
if err != nil {
log.Error("getVideoListByIDsFromRedis error", log.Any("ids", ids), log.Any("sInfo", sInfo), log.E(err))
return nil, err
}
videoInfo := parseVideoInfo(uInfo)
return videoInfo, 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())
_ = webg.Redis.Set(s, string(d), expire)
}
return nil
}
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)
}
func decComment(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)
}
+417
View File
@@ -0,0 +1,417 @@
package vidhelp
import (
"91porn-server/models/v/moduleconfmod"
"91porn-server/models/v/modulesectionmod"
"errors"
"math"
"reflect"
"strconv"
"strings"
"time"
"91porn-server/common/crypt"
"91porn-server/common/log"
sli "91porn-server/common/slice"
v10 "91porn-server/common/v10"
"91porn-server/models/l/payvidlgmod"
"91porn-server/models/v/modulevidmod"
"91porn-server/models/v/pushmod"
"91porn-server/models/v/tagmod"
"91porn-server/models/v/usermod"
"91porn-server/models/v/vidmod"
"github.com/shopspring/decimal"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// OID 别名
type OID = primitive.ObjectID
// Struct2Map 结构体转map
func Struct2Map(obj interface{}) map[string]interface{} {
t := reflect.TypeOf(obj)
v := reflect.ValueOf(obj)
var data = make(map[string]interface{})
for i := 0; i < t.NumField(); i++ {
key, ok := t.Field(i).Tag.Lookup("json")
if !ok {
log.Error("Struct2Map error", log.Any("obj", obj))
continue
}
data[key] = v.Field(i).Interface()
}
data["updatedAt"] = time.Now()
return data
}
// AddTags 没有的tag就添加
func AddTags(id string, tagNames []string) []primitive.ObjectID {
back := []primitive.ObjectID{}
cover, err := vidmod.GetVideoCover(id)
if err != nil {
return back
}
back = make([]primitive.ObjectID, 0, len(tagNames))
for _, n := range tagNames {
tMod := tagmod.Tag{
TagName: v10.ExtractPureChar(n),
CoverImg: cover,
Description: "",
SortCode: 0,
VPlayCount: 0,
TPlayCount: 0,
FakePlayCount: 0,
FakeCollCount: 0,
IsActive: true,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
data, err := tagmod.FindOneTagByName(v10.ExtractPureChar(n))
if err != nil {
log.Error("AddTags FindOneTagByName error", log.Any("id", id), log.Any("tagNames", tagNames), log.E(err))
continue
}
if data.ID.IsZero() {
oid, err := tagmod.InsertOne(&tMod)
if err != nil {
log.Error("AddTags InsertOne error", log.Any("id", id), log.Any("tagNames", tagNames), log.E(err))
} else {
back = append(back, oid)
}
continue
}
back = append(back, data.ID)
}
return back
}
// GetTagsByIDs2Map 通过tagid列表获取tag详情列表
func GetTagsByIDs2Map(ids []OID) map[primitive.ObjectID]vidmod.TagInfo {
m := make(map[primitive.ObjectID]vidmod.TagInfo)
tags, err := tagmod.FindTagsByIDS(ids)
if err != nil {
log.Error("GetTagsByIDs2Map error", log.Any("ids", ids), log.E(err))
return m
}
for _, info := range tags {
b := vidmod.TagInfo{
ID: info.ID,
Name: info.TagName,
CoverImg: info.CoverImg,
Description: info.Description,
}
m[info.ID] = b
}
return m
}
// GetPartTags 从内存获取tagInfo
func GetPartTags(ids []OID, mTags map[primitive.ObjectID]vidmod.TagInfo) []vidmod.TagInfo {
infos := make([]vidmod.TagInfo, len(ids))
for i, v := range ids {
infos[i] = mTags[v]
}
return infos
}
// Transfer2Info 将视频数据库模式转为返回模式
func transfer2Info(v *vidmod.VideoModel, uInfo usermod.BaseInfo, ti []vidmod.TagInfo, pushing bool) *vidmod.WebVideo {
isUser := false
if v.PublisherID > 115000 {
isUser = true
}
chosen := false
if v.Chosen {
chosen = v.Chosen
}
freeArea := false
if v.FreeArea {
freeArea = v.FreeArea
}
coins := v.Coins
if v.Status == 0 && coins == vidmod.PretendAccInitCoins {
coins = vidmod.PretendAccDefaultCoins
}
newsType := vidmod.SP
if len(v.NewsType) != 0 {
newsType = v.NewsType
}
tags := make([]string, len(ti))
for i, tag := range ti {
tags[i] = tag.Name
}
return &vidmod.WebVideo{
ID: v.ID,
NewsType: newsType,
Publisher: uInfo,
PubliserID: uInfo.UID,
PublisherName: uInfo.Name,
Title: v.Title,
Content: v.Content,
Tags: ti,
Cover: v.Cover,
CoverThumb: v.CoverThumb,
SeriesCover: v.SeriesCover,
IsUser: isUser,
PlayCount: v.PlayCount,
Status: v.Status,
PlayTime: v.PlayTime,
LikeCount: v.LikeCount,
Resolution: v.Resolution,
CommentCount: v.CommentCount,
ShareCount: v.ShareCount,
FakeLikeCount: v.FakeLikeCount,
FakeCommentCount: v.FakeCommentCount,
FakeShareCount: v.FakeShareCount,
FakePlayCount: v.FakePlayCount,
Coins: coins,
CreatedAt: v.CreatedAt,
UpdatedAt: v.UpdatedAt,
FreeTime: v.FreeTime,
SourceID: v.SourceID,
SourceURL: v.SourceURL,
LinkUrl: v.LinkUrl,
PreviewURL: v.PreviewURL,
SortCode: v.SortCode,
FileName: v.Filename,
Chosen: chosen,
FreeArea: freeArea,
Pushing: pushing,
Size: v.Size,
Reason: v.Reason,
IsTopping: v.IsTopping,
IsRecommend: v.IsRecommend,
IsChoosen: v.IsChoosen,
RecoWeight: v.RecoWeight,
ReviewAccount: v.ReviewAccount,
Rewarded: v.Rewarded,
FakeRewarded: v.FakeRewarded,
TagSort: coverTagSort(v.TagSort, ti),
LiaoBaTop: v.LiaoBaTop,
LiaoBaTopSort: v.LiaoBaTopSort,
WorksSort: v.WorksSort,
PageViewCount: v.PageViewCount,
TagsExport: strings.Join(tags, "|"),
ActivityID: v.ActivityID,
HappinessPlazaTop: v.HappinessPlazaTop,
ShareSort: v.ShareSort,
TimeNodeList: v.TimeNodeList,
DownloadAllow: v.DownloadAllow,
ShowType: v.ShowType,
}
}
func coverTagSort(id interface{}, ti []vidmod.TagInfo) []vidmod.TagSort {
tagSort := []vidmod.TagSort{}
tagInfo := make(map[string]int)
type keyValue struct {
Key string
Value int
}
xx := []keyValue{}
if err := crypt.Obj2Obj(id, &xx); err != nil {
return tagSort
}
if len(ti) > 0 {
ids := make([]string, len(xx))
for i, v := range xx {
ids[i] = v.Key
tagInfo[v.Key] = v.Value
}
tagSort = make([]vidmod.TagSort, len(ti))
for i, v := range ti {
var sortCode int
if sli.Contains(ids, v.ID.Hex()) {
sortCode = tagInfo[v.ID.Hex()]
}
tagSort[i] = vidmod.TagSort{
TagId: v.ID.Hex(),
TagName: v.Name,
SortCode: sortCode,
}
}
}
return tagSort
}
// GetUserInfo2Map 获取用户基本信息到map
func GetUserInfo2Map(uids []uint64) map[uint64]*usermod.BaseInfo {
userInfo, err := usermod.GetUsersBaseInfo(uids)
if err != nil {
return nil
}
m := make(map[uint64]*usermod.BaseInfo)
for _, i := range userInfo {
m[i.UID] = i
}
return m
}
// IsPushing 是否推送
func IsPushing(id primitive.ObjectID) bool {
push, _ := pushmod.IsExist(id)
return push
}
// EncodeVideoInfo 封装返回的视频列表信息
func EncodeVideoInfo(vInfos []*vidmod.VideoModel, secID primitive.ObjectID) (back []*vidmod.WebVideo, err error) {
vInfosLen := len(vInfos)
if vInfosLen == 0 {
return
}
uids := make([]uint64, vInfosLen)
var tags []primitive.ObjectID
videoIDs := make([]primitive.ObjectID, vInfosLen)
for i, v := range vInfos {
uids[i] = v.PublisherID
tags = append(tags, v.Tags...)
videoIDs[i] = v.ID
}
videoSalesStat, err := payvidlgmod.VideoCoinIncomeAggregateByVideoIDs(videoIDs)
if err != nil {
log.Error("VideoCoinIncomeAggregateByVideoIDs", log.E(err))
return
}
videoSalesMap := map[primitive.ObjectID]payvidlgmod.VideoCoinIncome{}
for _, stat := range videoSalesStat {
videoSalesMap[stat.VID] = stat
}
videos, err := modulevidmod.GetBySectionIDAndVids(secID, videoIDs)
if err != nil {
log.Error("GetBySectionIDAndVids", log.E(err))
return
}
videoMap := map[primitive.ObjectID]modulevidmod.SectionVideo{}
videoSectionMap := map[primitive.ObjectID]modulesectionmod.Section{}
moduleMap := map[primitive.ObjectID]moduleconfmod.ModuleConf{}
var secId []primitive.ObjectID
var moduleId []primitive.ObjectID
for _, v := range videos {
videoMap[v.VideoID] = v
secId = append(secId, v.SectionID)
}
// 获取专题信息
if len(secId) > 0 {
sections, err := modulesectionmod.GetBySectionByIDs(secId)
if err != nil {
log.Error("moduleSection GetBySectionByIDs err", log.E(err))
return nil, errors.New("moduleSection GetBySectionByIDs err")
}
if len(sections) > 0 {
for _, v := range videos {
for _, s := range sections {
if v.SectionID == s.ID {
videoSectionMap[v.VideoID] = s
moduleId = append(moduleId, s.SubModuleID)
}
}
}
}
// 获取模块信息
if len(moduleId) > 0 {
moduleConfs, err := moduleconfmod.FindByIDs(moduleId)
if err != nil {
log.Error("moduleConf FindByIDs err", log.E(err))
return nil, errors.New("moduleConf FindByIDs err")
}
if len(moduleConfs) > 0 {
for _, s := range sections {
for _, m := range moduleConfs {
if s.SubModuleID == m.ID {
moduleMap[s.ID] = m
}
}
}
}
}
}
mUser, _ := usermod.GetUsersBaseInfoMap(uids)
mTags := GetTagsByIDs2Map(tags)
back = make([]*vidmod.WebVideo, len(vInfos))
for i, v := range vInfos {
var uInfo usermod.BaseInfo
if mUser[v.PublisherID] != nil {
uInfo = *mUser[v.PublisherID]
}
info := transfer2Info(v, uInfo, GetPartTags(v.Tags, mTags), IsPushing(v.ID))
info.TotalSellAmount = videoSalesMap[v.ID].Total
info.TotalSellCount = videoSalesMap[v.ID].Count
info.TotalSellDays = CalculateVideoSellDays(v.ReviewAt)
info.PurchaseRate = CalculatePurchaseRate(videoSalesMap[v.ID].Count, int64(v.PlayCount))
info.HitRate = CalculateClickRate(int64(info.PlayCount), info.PageViewCount)
info.LikeRate = CalculateLikeRate(int64(info.LikeCount), int64(info.PlayCount))
info.SectionID = secID
info.SortCodeUnderSection = videoMap[v.ID].SortCode
info.ReviewAt = v.ReviewAt
info.SeedSize = v.SeedSize
info.SeedLinkUrl = v.SeedLinkUrl
info.SeedPlayTime = v.SeedPlayTime
info.RichText = v.RichText
info.PreviewStart = v.PreviewStart
info.SectionName = videoSectionMap[v.ID].SectionName
back[i] = info
if !videoSectionMap[v.ID].ID.IsZero() {
info.ModuleName = moduleMap[videoSectionMap[v.ID].ID].ModuleName
}
}
return
}
// IncCommentCount 评论统计次数加加
func IncCommentCount(id primitive.ObjectID) error {
if err := vidmod.IncCommentCount(id); err != nil {
return err
}
incComment(id)
return nil
}
// DecCommentCount 评论统计次数减少
func DecCommentCount(id primitive.ObjectID) error {
if err := vidmod.DecCommentCount(id, -1); err != nil {
return err
}
decComment(id)
return nil
}
// CalculateVideoSellDays 计算审核通过后天数
func CalculateVideoSellDays(reviewdAt time.Time) float64 {
if reviewdAt.IsZero() {
return 0
}
return math.Round(time.Since(reviewdAt).Hours() / 24)
}
// CalculatePurchaseRate 计算视频购买率
func CalculatePurchaseRate(sellCount, playCount int64) float64 {
if playCount <= 0 {
return 0.00
}
value, _ := strconv.ParseFloat(decimal.NewFromInt(sellCount).Div(decimal.NewFromInt(playCount)).Mul(decimal.NewFromInt(100)).StringFixed(2), 64)
return value
}
// CalculateClickRate 计算视频点击率
func CalculateClickRate(pageViewCount, playCount int64) float64 {
if playCount <= 0 {
return 0.00
}
value, _ := strconv.ParseFloat(decimal.NewFromInt(pageViewCount).Div(decimal.NewFromInt(playCount)).Mul(decimal.NewFromInt(100)).StringFixed(2), 64)
return value
}
// CalculateLikeRate 计算视频点赞率
func CalculateLikeRate(likeCount, playCount int64) float64 {
if playCount <= 0 {
return 0.00
}
value, _ := strconv.ParseFloat(decimal.NewFromInt(likeCount).Div(decimal.NewFromInt(playCount)).Mul(decimal.NewFromInt(100)).StringFixed(2), 64)
return value
}