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 }