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
+602
View File
@@ -0,0 +1,602 @@
package payvidlgmod
import (
"91porn-server/models/commod"
"fmt"
"math"
"time"
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/common/pageopt"
"91porn-server/common/timeutil"
"91porn-server/models"
"github.com/pkg/errors"
"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"
)
var mdb *db.MongoDB
const table = models.PayVideoLog
// initIndex 初始化索引
func initIndex() {
many := []mongo.IndexModel{
{
Keys: bson.D{{Key: "uid", Value: 1}, {Key: "videoID", Value: 1}},
Options: options.Index().SetUnique(true),
},
{
Keys: bson.D{{Key: "createdAt", Value: -1}},
},
{
Keys: bson.D{{Key: "uniq", Value: 1}},
Options: options.Index().SetUnique(true),
},
{
Keys: bson.D{{Key: "deductType", Value: 1}, {Key: "createdAt", Value: -1}},
Options: options.Index().SetSparse(true),
},
{
Keys: bson.D{{Key: "videoID", Value: 1}},
},
{
Keys: bson.D{{Key: "publisherID", Value: 1}},
},
}
if _, err := coll(nil).CreateIndex(many); err != nil {
panic(fmt.Sprintf("%s model set index err ==>[%+v]", table, err))
}
}
func coll(t *db.MongoTool) *db.MongoTool {
if t == nil {
return mdb.Coll(table)
}
return t.Coll(table)
}
// InsertVideoPayRecord 插入一条购买记录
func InsertVideoPayRecord(t *db.MongoTool, p Pay4VidLog) error {
p.Uniq = Unique(p.UID, p.VideoID)
p.CreatedAt = time.Now()
res, err := coll(t).InsertOne(&p)
if err != nil {
log.Error("InsertVideoPayRecord error", log.Any("p", p), log.E(err))
return err
}
p.ID = res.InsertedID.(ObjectID)
return nil
}
// IsPay4Video 用户是否购买了视频
func IsPay4Video(uid uint64, videoID ObjectID) (bool, error) {
cnt, err := coll(nil).Count(bson.M{"uid": uid, "videoID": videoID})
if err != nil {
log.Error("IsPay4Video error", log.Any("uid", uid), log.Any("videoID", videoID), log.E(err))
return false, err
}
return cnt != 0, nil
}
func FindManyPay4VidLogByUID(uid uint64, newsType string) ([]*Pay4VidLog, error) {
vl := make([]*Pay4VidLog, 0)
err := coll(nil).Find(&vl, bson.M{"uid": uid, "newsType": newsType})
if err != nil {
log.Error("FindManyPay4VidLogByUID error", log.Any("uid", uid), log.Any("err", err.Error()))
return nil, err
}
return vl, nil
}
// IsPay4Videos 用户是否购买了视频
func IsPay4Videos(uid uint64, videoIDs []ObjectID) (map[ObjectID]bool, error) {
if videoIDs == nil {
videoIDs = []ObjectID{}
}
m := make(map[ObjectID]bool)
var infos []Pay4VidLog
query := bson.M{"uid": uid, "videoID": bson.M{"$in": videoIDs}}
if err := coll(nil).Find(&infos, query); err != nil {
log.Error("IsPay4Videos error", log.Any("uid", uid), log.Any("videoIDs", videoIDs), log.E(err))
return m, err
}
for _, i := range infos {
m[i.VideoID] = true
}
return m, nil
}
// 判断当前视频是否有人购买过
func IsVidIfBePay(id primitive.ObjectID) bool {
var pay *Pay4VidLog
if err := coll(nil).FindOne(&pay, bson.M{"videoID": id}); err != nil {
log.Error("IsVidIfBePay error", log.Any("videoID", id), log.E(err))
return false
}
return pay != nil
}
// DelVideoPayRecord 删除一条购买记录
func DelVideoPayRecord(videoID primitive.ObjectID, uid uint64) error {
if _, err := coll(nil).DeleteOne(bson.M{"videoID": videoID, "uid": uid}); err != nil {
log.Error("DelVideoPayRecord DeleteOne error", log.Any("videoID", videoID), log.Any("uid", uid))
return err
}
return nil
}
// 从给定的hash列表中获取购买状态映射:uniq->Statue
// uniq通过Unique()获取
func PayStatueMap(uniqList []string) (map[string]bool, error) {
filter := bson.M{
"uniq": bson.M{"$in": uniqList},
}
payLogList := make([]Pay4VidLog, 0, len(uniqList))
if err := coll(nil).Find(&payLogList, filter); err != nil {
return nil, err
}
m := make(map[string]bool, len(payLogList))
//初始化
for _, uniq := range uniqList {
m[uniq] = false
}
//已经收藏的
for _, v := range payLogList {
uniq := v.Uniq
m[uniq] = true
}
return m, nil
}
func GetUIDListByPayTime(start time.Time, end time.Time) ([]uint64, error) {
filter := bson.M{
"createdAt": bson.M{
"$gte": start,
"$lt": end,
},
}
list := make([]struct {
UID uint64 `bson:"uid"`
}, 0)
if err := coll(nil).Find(&list, filter); err != nil {
return nil, err
}
uidList := make([]uint64, len(list))
for i, v := range list {
uidList[i] = v.UID
}
return uidList, nil
}
func GetPayMoneyByTime(start time.Time, end time.Time) (int64, error) {
filter := bson.M{
"createdAt": bson.M{
"$gte": start,
"$lt": end,
},
}
logs := make([]Pay4VidLog, 0)
if err := coll(nil).Find(&logs, filter); err != nil {
return 0, fmt.Errorf("table:%s GetPayMoneyByTime err: %s", table, err.Error())
}
total := int64(0)
for _, v := range logs {
total += v.PayMoney
}
return total, nil
}
// FindManyVideoPayRecord 查询所有
func FindThreeMonthVideoPayRecord(publisherID uint64, pageNumber, pageSize uint64) (total int64, totalAmount int64, data []*Pay4VidLog, hasNext bool, err error) {
data = make([]*Pay4VidLog, 0)
startTime := timeutil.NearMonth(time.Now(), 2)
f := bson.M{"publisherID": publisherID, "createdAt": bson.M{"$gte": startTime}}
skip := int64(pageSize * (pageNumber - 1))
limit := int64(pageSize + 1)
opts := options.FindOptions{
Skip: &skip,
Limit: &limit,
Sort: bson.D{{Key: "createdAt", Value: -1}},
}
if err = coll(nil).Find(&data, f, &opts); err != nil {
log.Error("FindManyVideoPayRecord error", log.Any("filter", f), log.E(err))
}
type Res struct {
TotalAmount float64 `json:"totalAmount" bson:"totalAmount"`
}
totalIncome := []bson.M{
{"$match": f},
{
"$group": bson.M{
"_id": nil,
"totalAmount": bson.M{
"$sum": "$publisherIncome",
},
},
},
}
ress := make([]Res, 0)
if err = coll(nil).Aggregate(&ress, totalIncome); err != nil {
log.Error("FindManyVideoPayRecord error", log.Any("filter", f), log.E(err))
}
if len(ress) > 0 {
totalAmount = int64(math.Floor(ress[0].TotalAmount))
}
total, err = coll(nil).Count(f)
if err != nil {
log.Error("FindManyVideoPayRecord Count error", log.Any("filter", f), log.E(err))
}
if len(data) > int(pageSize) {
hasNext = true
data = data[:pageSize]
}
return
}
// GetWorksIncomeList 查询收益列表
func GetWorksIncomeList(publisherID uint64, page commod.Page) (data []*Pay4VidLog, hasNext bool, err error) {
data = make([]*Pay4VidLog, 0)
opt := options.Find().
SetLimit(page.Limit64() + 1).
SetSkip(page.Skip64()).
SetSort(bson.D{{"createdAt", -1}})
filter := bson.M{
"publisherID": publisherID,
}
if err = coll(nil).Find(&data, filter, opt); err != nil {
log.Error("FindManyVideoPayRecord error", log.Any("filter", filter), log.E(err))
return nil, false, err
}
hasNext = len(data) > int(page.Limit64())
if hasNext {
data = data[:page.Limit64()]
}
return
}
// PayCoinByCreatedTime 金币
func PayCoinByCreatedTime(start time.Time, end time.Time) (int64, error) {
pipeline := []bson.M{
{
"$match": bson.M{
// createdAt ∈ [startTime, endTime)
"createdAt": bson.M{
"$gte": start,
"$lt": end,
},
},
},
{
"$group": bson.M{
"_id": nil,
"totalAmount": bson.M{"$sum": "$payMoney"},
},
},
}
var ret struct {
TotalAmount int64 `bson:"totalAmount"`
}
if err := coll(nil).AggregateDecode(&ret, pipeline); err != nil {
return 0, fmt.Errorf("table:%s PayCoinByCreatedTime err: %s", table, err.Error())
}
return ret.TotalAmount, nil
}
// PayCoinMapByTime vid->金币 map
func PayCoinMapByTime(start time.Time, end time.Time) (map[ObjectID]int64, error) {
pipeline := []bson.M{
{
"$match": bson.M{
"createdAt": bson.M{
"$gte": start,
"$lt": end,
},
},
},
{
"$group": bson.M{
"_id": "$videoID",
"income": bson.M{
"$sum": "$payMoney",
},
},
},
}
var docList []struct {
VID ObjectID `bson:"_id"`
Income int64 `bson:"income"`
}
if err := coll(nil).Aggregate(&docList, pipeline); err != nil {
return nil, fmt.Errorf("table:%s PayCoinMapByTime err: %s", table, err.Error())
}
ret := make(map[ObjectID]int64)
for _, doc := range docList {
ret[doc.VID] = doc.Income
}
return ret, nil
}
func VideoCoinIncomeGross(start, end time.Time) (int64, error) {
matchStage := bson.M{
"$match": bson.M{
"createdAt": bson.M{
"$gte": start,
"$lt": end,
},
},
}
groupStage := bson.M{
"$group": bson.M{
"_id": nil,
"gross": bson.M{
"$sum": "$payMoney",
},
},
}
var gross struct {
Gross int64 `bson:"gross"`
}
pipeline := []bson.M{matchStage, groupStage}
// opts := options.Aggregate()
if err := coll(nil).AggregateDecode(&gross, pipeline); err != nil {
log.Error("VideoCoinIncomeGross err: " + err.Error())
return 0, errors.Wrapf(err, "table:%s VideoCoinIncomeGross", table)
}
return gross.Gross, nil
}
// VideoCoinIncomeAggregateByVideoIDs 根据视频ID统计视频的售卖总数以及总金币数
func VideoCoinIncomeAggregateByVideoIDs(ids []primitive.ObjectID) (list []VideoCoinIncome, err error) {
if len(ids) == 0 {
return
}
matchStage := bson.M{
"$match": bson.M{
"videoID": bson.M{"$in": ids},
},
}
groupStage := bson.M{
"$group": bson.M{
"_id": "$videoID",
"total": bson.M{
"$sum": "$payMoney",
},
"count": bson.M{
"$sum": 1,
},
},
}
pipeline := []bson.M{matchStage, groupStage}
opts := options.Aggregate().SetMaxTime(10 * time.Second)
if err := coll(nil).Aggregate(&list, pipeline, opts); err != nil {
return nil, errors.Wrapf(err, "table:%s VideoCoinIncomeAggregateByVideoIDs", table)
}
return
}
// VideoCoinIncomeAggregate 聚合统计在给定时间段内视频收入排名前N的视频
func VideoCoinIncomeAggregateTopN(topN int, start, end time.Time) (list []VideoCoinIncome, err error) {
matchStage := bson.M{
"$match": bson.M{
"createdAt": bson.M{
"$gte": start,
"$lt": end,
},
},
}
groupStage := bson.M{
"$group": bson.M{
"_id": "$videoID",
"total": bson.M{
"$sum": "$payMoney",
},
"count": bson.M{
"$sum": 1,
},
},
}
sortStage := bson.M{
"$sort": bson.M{
"total": -1,
"count": -1,
},
}
limitStage := bson.M{
"$limit": topN,
}
pipeline := []bson.M{matchStage, groupStage, sortStage, limitStage}
opts := options.Aggregate().SetMaxTime(10 * time.Second)
if err := coll(nil).Aggregate(&list, pipeline, opts); err != nil {
return nil, errors.Wrapf(err, "table:%s VideoCoinIncomeAggregateTopN", table)
}
return
}
// PayCountMapByTime vid->金币 map
func PayCountMapByTime(start time.Time, end time.Time) (map[ObjectID]int64, error) {
pipeline := []bson.M{
{
"$match": bson.M{
"createdAt": bson.M{
"$gte": start,
"$lt": end,
},
},
},
{
"$group": bson.M{
"_id": "$videoID",
"count": bson.M{
"$sum": 1,
},
},
},
}
var docList []struct {
VID ObjectID `bson:"_id"`
Count int64 `bson:"count"`
}
if err := coll(nil).Aggregate(&docList, pipeline); err != nil {
return nil, fmt.Errorf("table:%s PayCoinMapByTime err: %s", table, err.Error())
}
ret := make(map[ObjectID]int64)
for _, doc := range docList {
ret[doc.VID] = doc.Count
}
return ret, nil
}
// TaxAmountMapByTime vid->TaxAmount map
func TaxAmountMapByTime(start time.Time, end time.Time) (map[ObjectID]float64, error) {
pipeline := []bson.M{
{
"$match": bson.M{
"createdAt": bson.M{
"$gte": start,
"$lt": end,
},
},
},
{
"$group": bson.M{
"_id": "$videoID",
"taxAmount": bson.M{
"$sum": "$taxAmount",
},
},
},
}
var docList []struct {
VID ObjectID `bson:"_id"`
TaxAmount float64 `bson:"taxAmount"`
}
if err := coll(nil).Aggregate(&docList, pipeline); err != nil {
return nil, fmt.Errorf("table:%s PayCoinMapByTime err: %s", table, err.Error())
}
ret := make(map[ObjectID]float64)
for _, doc := range docList {
ret[doc.VID] += doc.TaxAmount
}
return ret, nil
}
func PublisherIncomeMapByTime(start time.Time, end time.Time) (map[uint64]int64, error) {
pipeline := []bson.M{
{
"$match": bson.M{
"createdAt": bson.M{
"$gte": start,
"$lt": end,
},
},
},
{
"$group": bson.M{
"_id": "$publisherID",
"income": bson.M{
"$sum": "$payMoney",
},
},
},
}
var docList []struct {
PublisherID uint64 `bson:"_id"`
Income int64 `bson:"income"`
}
if err := coll(nil).Aggregate(&docList, pipeline); err != nil {
return nil, fmt.Errorf("table:%s PublisherIncomeMapByTime err: %s", table, err.Error())
}
ret := make(map[uint64]int64)
for _, doc := range docList {
ret[doc.PublisherID] = doc.Income
}
return ret, nil
}
// TaxAmountByTime
func TaxAmountByTime(start, end time.Time, mats ...Matcher) (float64, error) {
mats = append(mats, (&CreatedAtGTEAndLTMatch{GTE: &start, LT: &end}).New())
filter := pageopt.MergeM(mats)
var list []struct {
TaxAmount float64 `bson:"taxAmount"` //税额
}
opt := (&options.FindOptions{}).SetProjection(M{
"taxAmount": 1,
})
if err := coll(nil).Find(&list, filter, opt); err != nil {
return 0, err
}
var taxAmount float64
for _, v := range list {
taxAmount += v.TaxAmount
}
return taxAmount, nil
}
func FindByUID(uid uint64, newsType string, pageNumber int64, pageSize int64) (data []*Pay4VidLog, hasNext bool, err error) {
data = make([]*Pay4VidLog, 0)
opt := options.Find().
SetLimit(pageSize + 1).
SetSkip((pageNumber - 1) * pageSize).
SetSort(bson.D{{"createdAt", -1}})
filter := bson.M{
"uid": uid,
"newsType": newsType,
}
err = coll(nil).Find(&data, filter, opt)
if err != nil {
log.Error("FindByUID error", log.Any("uid", uid), log.Any("err", err.Error()))
return nil, false, err
}
if len(data) > int(pageSize) {
hasNext = true
data = data[:pageSize]
}
return
}
func PublisherVideoDeductionRange(uid uint64, start, end time.Time) ([]PublisherDeductionStat, error) {
pipeline := []bson.M{
{
"$match": bson.M{
// createdAt ∈ [startTime, endTime)
"createdAt": bson.M{
"$gte": start,
"$lt": end,
},
"publisherID": uid,
},
},
{
"$group": bson.M{
"_id": "$isVideoDeduction",
"totalAmount": bson.M{"$sum": "$coins"},
"videoCount": bson.M{"$sum": 1},
},
},
}
docList := []PublisherDeductionStat{}
return docList, coll(nil).Aggregate(&docList, pipeline)
}
func GetPayVidLogsByTime(start time.Time, end time.Time) ([]Pay4VidLogShort, error) {
filter := bson.M{
"createdAt": bson.M{
"$gte": start,
"$lt": end,
},
}
logs := make([]Pay4VidLogShort, 0)
if err := coll(nil).Find(&logs, filter); err != nil {
return logs, fmt.Errorf("table:%s GetPayMoneyByTime err: %s", table, err.Error())
}
return logs, nil
}