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
@@ -0,0 +1,121 @@
package dailyretentionmod
import (
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/models"
"errors"
"fmt"
"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 table = models.DailyRetention
func coll(t *db.MongoTool) *db.MongoTool {
if t == nil {
return mdb.Coll(table)
}
return t.Coll(table)
}
func initIndex() {
many := []mongo.IndexModel{
{
Keys: bson.D{
{Key: "date", Value: -1},
},
},
{
Keys: bson.D{
{Key: "createdAt", Value: -1},
},
},
{
Keys: bson.D{
{Key: "adGroup", Value: 1},
{Key: "date", Value: -1},
},
Options: options.Index().SetUnique(true),
},
}
if _, err := coll(nil).CreateIndex(many); err != nil {
panic(fmt.Sprintf("%s model set index err ==>[%+v]", table, err))
}
}
// Init 初始化索引
func Init() {
mdb = db.Init(table)
initIndex()
}
// QueryAllList 分页查询文档
func QueryAllList(filter primitive.M, opts ...*options.FindOptions) (out []*DailyRetention, err error) {
if err = coll(nil).Find(&out, filter, opts...); err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "QueryAllList", table, "Find", err),
log.Any("filter", filter),
log.Any("opts", opts),
)
return nil, err
}
return
}
// QueryOne
func QueryOne(filter bson.M) (*DailyRetention, error) {
v := DailyRetention{}
if err := coll(nil).FindOne(&v, filter); err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "QueryOne", table, "FindOne", err),
log.Any("filter", filter),
)
return &v, err
}
if v.Id.IsZero() {
return nil, errors.New("record not found")
}
return &v, nil
}
// UpsertOne
func UpsertOne(filter bson.M, updateData bson.M) (err error) {
if _, err = coll(nil).UpsertOne(filter, updateData); err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "UpsertOne", table, "UpdateOne", err),
log.Any("filter", filter),
log.Any("updateData", updateData),
)
return
}
return
}
func BulkWrite(models []mongo.WriteModel, opts ...*options.BulkWriteOptions) (err error) {
if len(models) == 0 {
return nil
}
if _, err = coll(nil).Bulk(models, opts...); err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "BulkWrite", table, "BulkWrite", err),
log.Any("modelsCount", len(models)),
)
return
}
return
}
func Distinct(filter bson.M, fieldName string, opts ...*options.DistinctOptions) (out []*DailyRetention, err error) {
raw, err := coll(nil).Distinct(fieldName, filter, opts...)
if err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "Distinct", table, "UpdateOne", err),
log.Any("fieldName", fieldName),
log.Any("filter", filter),
)
return
}
out = make([]*DailyRetention, len(raw))
return
}
+33
View File
@@ -0,0 +1,33 @@
package dailyretentionmod
import (
"91porn-server/common/db"
"91porn-server/models/commod"
"time"
"go.mongodb.org/mongo-driver/bson/primitive"
)
var mdb *db.MongoDB
const (
MaxRetentionDays = 30 // 最大留存天数
)
// DailyRetention 每日用户留存统计
// @table:daily_retention
// @model:DailyRetentionModel
type DailyRetention struct {
Id primitive.ObjectID `json:"id" bson:"_id"` // 自增ID
Date time.Time `json:"date" bson:"date" index:"date_idx, date_adgroup_idx:unique"` // 日期,当日0点
AdGroup commod.AdGroup `json:"adGroup" bson:"adGroup" index:"adgroup_idx, date_adgroup_idx:unique"` // 广告组
NewUsers int64 `json:"newUsers" bson:"newUsers"` // 当天新增用户
UserRetained map[int]int64 `json:"userRetained" bson:"userRetained"` // key = 第几天, value = 日活留存人数
UserRates map[int]float64 `json:"userRates" bson:"userRates"` // key = 第几天, value = 日活留存率
AdClickAcc map[int]int64 `json:"adClickAcc" bson:"adClickAcc"` // key = 第几天, value = 广告点击累计
TotalClickAcc map[int]int64 `json:"totalClickAcc" bson:"totalClickAcc"` // key = 第几天, value = 总点击累计
PayCountAcc map[int]int64 `json:"payCountAcc" bson:"payCountAcc"` // key = 第几天, value = 充值次数累计
PayTotalAcc map[int]int64 `json:"payTotalAcc" bson:"payTotalAcc"` // key = 第几天, value = 充值总金额累计
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"` // 更新时间
CreatedAt time.Time `json:"createdAt" bson:"createdAt" index:"-created_at_idx"` // 创建时间
}
+203
View File
@@ -0,0 +1,203 @@
package hotstatmod
import (
"fmt"
"time"
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/common/timeutil/timerange"
"91porn-server/models"
"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.NewsHot
func coll(t *db.MongoTool) *db.MongoTool {
if t == nil {
return mdb.Coll(table)
}
return t.Coll(table)
}
// InitIndex 设置index
func initIndex() {
many := []mongo.IndexModel{
{
Keys: bson.D{{Key: "sumDate", Value: 1}, {Key: "videoID", Value: 1}},
Options: options.Index(),
},
{
Keys: bson.D{{Key: "sumDate", Value: 1}, {Key: "todayHot", Value: -1}},
Options: options.Index(),
},
{
Keys: bson.D{{Key: "sumDate", Value: 1}, {Key: "weekHot", Value: -1}},
Options: options.Index(),
},
{
Keys: bson.D{{Key: "sumDate", Value: 1}, {Key: "monthHot", Value: -1}},
Options: options.Index(),
},
{
Keys: bson.D{{Key: "createdAt", Value: 1}},
Options: options.Index(),
},
}
if _, err := coll(nil).CreateIndex(many); err != nil {
panic(fmt.Sprintf("%s model set index err ==>[%+v]", table, err))
}
}
// UpdateInsertHotValue 更新热度数值,没有就插入
func UpdateInsertHotValue(sumDate time.Time, mDayli map[primitive.ObjectID]int, mWeek map[primitive.ObjectID]int, mMonth map[primitive.ObjectID]int, mNow map[primitive.ObjectID]int) error {
writes := make([]mongo.WriteModel, len(mNow))
i := 0
for vid, v := range mNow {
filter := bson.M{
"sumDate": sumDate,
"videoID": vid,
}
update := bson.M{
"$setOnInsert": bson.M{
"sumDate": sumDate,
"videoID": vid,
"createdAt": time.Now(),
},
"$set": bson.M{
"todayHot": mDayli[vid],
"weekHot": mWeek[vid],
"monthHot": mMonth[vid],
"totalHot": v,
"updatedAt": time.Now(),
},
}
writes[i] = mongo.NewUpdateOneModel().SetFilter(filter).SetUpdate(update).SetUpsert(true)
i++
log.Debug(fmt.Sprintf("sumDate:%s table:%s [filter:%+v update:%+v ]\n", sumDate, table, filter, update))
}
if len(writes) == 0 {
return nil
}
//bulkWrite 不是原子操作 不具备事务性
opt := (&options.BulkWriteOptions{}).SetOrdered(false) //设为无序,触发并行写,提升写效率
_, err := coll(nil).Bulk(writes, opt)
return err
}
func getHotValue(page, size uint64, cond bson.M, sort bson.D) ([]*HotStat, bool, error) {
hasNext := false
skip := (page - 1) * size
opts := options.FindOptions{}
if sort != nil {
opts.SetSort(sort)
}
opts.SetSkip(int64(skip)).SetLimit(int64(size + 1))
var back []*HotStat
if err := coll(nil).Find(&back, cond, &opts); err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "getHotValue", table, "Find", err),
log.Any("page", page),
log.Any("size", size),
log.Any("cond", cond),
log.Any("sort", sort),
)
return nil, hasNext, err
}
if uint64(len(back)) > size {
hasNext = true
back = back[:size]
}
return back, hasNext, nil
}
func getSortedVideoID(page, size uint64, cond bson.M, sort bson.D) ([]primitive.ObjectID, bool, error) {
ids := []primitive.ObjectID{}
infos, hasNext, err := getHotValue(page, size, cond, sort)
if err != nil {
return ids, false, err
}
ids = make([]primitive.ObjectID, len(infos))
for i, v := range infos {
ids[i] = v.VideoID
}
return ids, hasNext, err
}
// GetHotValueToday 查找热度值
func GetHotValueToday(page, size uint64) ([]primitive.ObjectID, bool, error) {
sumDate := timerange.LocDayRange(time.Now()).Head
cond := bson.M{"sumDate": sumDate}
sort := bson.D{{Key: "todayHot", Value: -1}}
return getSortedVideoID(page, size, cond, sort)
}
// GetHotValueWeek 查找热度值
func GetHotValueWeek(page, size uint64) ([]primitive.ObjectID, bool, error) {
sumDate := timerange.LocDayRange(time.Now()).Head
cond := bson.M{"sumDate": sumDate}
sort := bson.D{{Key: "weekHot", Value: -1}}
return getSortedVideoID(page, size, cond, sort)
}
// GetHotValueMonth 查找热度值
func GetHotValueMonth(page, size uint64) ([]primitive.ObjectID, bool, error) {
sumDate := timerange.LocDayRange(time.Now()).Head
cond := bson.M{"sumDate": sumDate}
sort := bson.D{{Key: "monthHot", Value: -1}}
return getSortedVideoID(page, size, cond, sort)
}
func getHotValueByDate(dt time.Time) (map[primitive.ObjectID]int, error) {
var infos []*HotStat
mValue := make(map[primitive.ObjectID]int)
if err := coll(nil).Find(&infos, bson.M{"sumDate": dt}); err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "getHotValue", table, "Find", err))
return mValue, err
}
for _, v := range infos {
mValue[v.VideoID] = v.TotalHot
}
return mValue, nil
}
// GetYesterdayHotValue 获取昨日热度值
func GetYesterdayHotValue(dt time.Time) (map[primitive.ObjectID]int, error) {
return getHotValueByDate(dt.AddDate(0, 0, -1))
}
// GetLastWeekHotValue 获取上个周日热度值
func GetLastWeekHotValue(dt time.Time) (map[primitive.ObjectID]int, error) {
offset := 0
wk := dt.Weekday()
switch wk {
case time.Monday:
offset = -1
case time.Tuesday:
offset = -2
case time.Wednesday:
offset = -3
case time.Thursday:
offset = -4
case time.Friday:
offset = -5
case time.Saturday:
offset = -6
}
return getHotValueByDate(dt.AddDate(0, 0, offset))
}
// GetLastMonthHotValue 获取上个月末热度值
func GetLastMonthHotValue(dt time.Time) (map[primitive.ObjectID]int, error) {
return getHotValueByDate(dt.AddDate(0, 0, -dt.Day()))
}
func DeleteBeforeCreatedAt(t *db.MongoTool, tm time.Time) error {
_, err := coll(t).DeleteMany(bson.M{"createdAt": bson.M{"$lt": tm}})
return err
}
+27
View File
@@ -0,0 +1,27 @@
package hotstatmod
import (
"time"
"91porn-server/common/db"
"go.mongodb.org/mongo-driver/bson/primitive"
)
func Init() {
mdb = db.Init(table)
initIndex()
}
// HotStat 帖子热度统计表
type HotStat struct {
ID primitive.ObjectID `json:"_id" bson:"_id,omitempty"`
SumDate time.Time `json:"sumDate" bson:"sumDate"`
VideoID primitive.ObjectID `json:"videoID" bson:"videoID"`
TodayHot int `json:"todayHot" bson:"todayHot"` //今日热度
WeekHot int `json:"weekHot" bson:"weekHot"` //本周热度
MonthHot int `json:"monthHot" bson:"monthHot"` //本月热度
TotalHot int `json:"totalHot" bson:"totalHot"` //目前为止总热度
CreatedAt time.Time `json:"createdAt" bson:"createdAt"` // 创建时间
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"` // 更新时间
}
+176
View File
@@ -0,0 +1,176 @@
package kwrankmod
import (
"fmt"
"math/rand"
"time"
"91porn-server/common"
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/common/timeutil/timerange"
"91porn-server/models"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var mdb *db.MongoDB
const table = models.KWRankStat
func coll(t *db.MongoTool) *db.MongoTool {
if t == nil {
return mdb.Coll(table)
}
return t.Coll(table)
}
// InitIndex 设置index
func initIndex() {
many := []mongo.IndexModel{
{
Keys: bson.D{{Key: "word", Value: 1}},
},
{
Keys: bson.D{{Key: "sumDate", Value: -1}},
},
{
Keys: bson.D{{Key: "sumDate", Value: -1}, {Key: "word", Value: 1}},
Options: options.Index().SetUnique(true),
},
{
Keys: bson.D{{Key: "count", Value: -1}},
},
{
Keys: bson.D{{Key: "sortKey", Value: -1}}, //大的排前面
},
{
Keys: bson.D{{Key: "sumDate", Value: -1}, {Key: "sortKey", Value: -1}, {Key: "count", Value: -1}}, //大的排前面
},
{
Keys: bson.D{{Key: "recordAt", Value: -1}},
},
}
if _, err := coll(nil).CreateIndex(many); err != nil {
panic(fmt.Sprintf("%s model set index err ==>[%+v]", table, err))
}
}
func Insert(kw Word) error {
now := time.Now()
kw.CreatedAt = now
kw.UpdatedAt = now
if _, err := coll(nil).InsertOne(kw); err != nil {
log.ZapLog.Error(fmt.Sprintf("coll:%s Insert fail error:%+v:", table, err))
return err
}
return nil
}
func Update(id ObjectID, doc RankDoc) error {
update, err := common.ToBsonM(doc)
if err != nil {
return fmt.Errorf("coll:%s update ToBsonM fail, error:%+v ", table, err)
}
update["updatedAt"] = time.Now()
if _, err := coll(nil).UpdateOne(M{"_id": id}, M{"$set": update}); err != nil {
log.ZapLog.Error(fmt.Sprintf("coll:%s update fail error:%+v:", table, err))
return err
}
return nil
}
// DeleteOne DeleteOne
func Delete(id ObjectID) error {
_, err := coll(nil).DeleteOne(M{"_id": id})
if err != nil {
log.ZapLog.Error(fmt.Sprintf("coll:%s DeleteOne fail error:%+v:", table, err))
return err
}
return nil
}
var rankSort = bson.D{
{Key: "sumDate", Value: -1},
{Key: "sortKey", Value: -1},
{Key: "count", Value: -1},
}
var enableIsTrue = true
// RecentRanking RecentRanking
func RecentRanking(limit int64) ([]Keyword, error) {
kws, err := KwsList(rankSort, 0, limit, (&EnableMatch{&enableIsTrue}).New())
if err != nil {
log.ZapLog.Error(fmt.Sprintf("coll:%s RecentRanking fail error:%+v:", table, err))
}
return kws, err
}
func ChangeStatTrans(trans *db.MongoTool, position time.Time, recordTime time.Time, setList []RankSetDoc) error {
if len(setList) == 0 {
return nil
}
sumDate := timerange.LocDayRange(position).Head
// create the slice of write models
writes := make([]mongo.WriteModel, len(setList))
for i, doc := range setList {
filter := M{
"sumDate": sumDate,
"word": doc.Word,
}
update := M{
"$setOnInsert": M{
"sumDate": sumDate,
"word": doc.Word,
"enable": true, //首次插入时使能
"createdAt": time.Now(),
},
"$set": M{
"count": doc.Count, //设计为统计表keywordStat聚合word得到
"sortKey": doc.Count, //控制app端排序
"recordAt": recordTime,
"updatedAt": time.Now(),
},
}
writes[i] = mongo.NewUpdateOneModel().
SetFilter(filter).
SetUpdate(update).
SetUpsert(true)
log.Debug(fmt.Sprintf("sumDate:%s table:%s [ word:%+v filter:%+v update:%+v ]\n", sumDate, table, doc.Word, filter, update))
}
//bulkWrite 不是原子操作 不具备事务性
opt := (&options.BulkWriteOptions{}).SetOrdered(false) //设为无序,触发并行写,提升写效率
_, err := coll(trans).Bulk(writes, opt)
return err
}
func GetListNewest(limit int64) (WordArray, error) {
sort := bson.D{{Key: "createdAt", Value: -1}}
enable := true
enableMatch := EnableMatch{&enable}
return List(sort, 0, limit, enableMatch.New())
}
func GetListRandom(limit int64) (WordArray, error) {
enable := true
enableMatch := EnableMatch{&enable}
count, err := Count(enableMatch.New())
if err != nil {
return nil, err
}
maxSkip := count - limit
if maxSkip < 0 {
maxSkip = 0
}
randSkip := rand.Int63n(maxSkip)
sort := bson.D{{Key: "count", Value: -1}}
return List(sort, randSkip, limit)
}
func DeleteBeforeSumDate(t *db.MongoTool, tm time.Time) error {
_, err := coll(t).DeleteMany(bson.M{"sumDate": bson.M{"$lt": tm}})
return err
}
+78
View File
@@ -0,0 +1,78 @@
package kwrankmod
import (
"fmt"
"time"
"91porn-server/common/log"
"91porn-server/common/pageopt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
type Matcher = pageopt.Matcher
// SumDateMatch
type SumDateMatch struct {
SumDate *time.Time
}
func (i *SumDateMatch) New() Matcher {
return pageopt.NewAssignMatch("sumDate", i.SumDate)
}
// EnableMatch
type EnableMatch struct {
Enable *bool
}
func (i *EnableMatch) New() Matcher {
return pageopt.NewAssignMatch("enable", i.Enable)
}
func List(sort bson.D, skip, limit int64, matchers ...Matcher) (WordArray, error) {
filter := pageopt.MergeM(matchers)
opt := &options.FindOptions{}
if len(sort) != 0 {
opt.SetSort(sort)
}
opt.SetSkip(skip)
opt.SetLimit(limit)
wordArray := make(WordArray, 0, limit)
if err := coll(nil).Find(&wordArray, filter, opt); err != nil {
log.ZapLog.Error(fmt.Sprintf("coll:%s List fail error:%+v:", table, err))
return wordArray, err
}
return wordArray, nil
}
type Keyword struct {
KeyWord string `bson:"word" json:"keyWord"` //关键字
Count int64 `bson:"count" json:"count"` //关键字搜索次数
}
func KwsList(sort bson.D, skip, limit int64, matchers ...Matcher) ([]Keyword, error) {
filter := pageopt.MergeM(matchers)
opt := &options.FindOptions{}
if len(sort) != 0 {
opt.SetSort(sort)
}
opt.SetSkip(skip)
opt.SetLimit(limit)
opt.SetProjection(M{
"word": 1,
"count": 1,
})
list := make([]Keyword, 0, limit)
if err := coll(nil).Find(&list, filter, opt); err != nil {
log.ZapLog.Error(fmt.Sprintf("coll:%s List fail error:%+v:", table, err))
return list, err
}
return list, nil
}
func Count(matchers ...Matcher) (int64, error) {
filter := pageopt.MergeM(matchers)
return coll(nil).Count(filter)
}
+66
View File
@@ -0,0 +1,66 @@
package kwrankmod
import (
"time"
"91porn-server/common/db"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type ObjectID = primitive.ObjectID
type M = bson.M
type Word struct {
ID primitive.ObjectID `bson:"_id,omitempty"` // ID
SumDate time.Time `bson:"sumDate"`
ComplexProportion float64 `bson:"complexProportion"` //综合搜索占比
VideoProportion float64 `bson:"videoProportion"` //视屏搜索占比
UserProportion float64 `bson:"userProportion"` //用户搜索占比
TagProportion float64 `bson:"tagProportion"` //标签(话题)搜索占比
LocationProportion float64 `bson:"locationProportion"` //地点搜索占比
Word string `bson:"word"` //词
Count int64 `bson:"count"` //搜索量
SortKey int64 `bson:"sortKey"` //排序量
Enable bool `bson:"enable"` //使能
RecordAt time.Time `bson:"recordAt"` //数据记录时间
CreatedAt time.Time `bson:"createdAt"` // 创建时间
UpdatedAt time.Time `bson:"updatedAt"` // 更新时间
}
type RankDoc struct {
Word *string `bson:"word,omitempty"`
SortKey *int64 `bson:"sortKey,omitempty"`
Enable *bool `bson:"enable,omitempty"`
}
type RankSetDoc struct {
Word *string `bson:"word,omitempty"`
Count *int64 `bson:"count,omitempty"`
ComplexProportion *float64 `bson:"complexProportion"`
VideoProportion *float64 `bson:"videoProportion"`
UserProportion *float64 `bson:"userProportion"`
TagProportion *float64 `bson:"tagProportion"`
LocationProportion *float64 `bson:"locationProportion"`
}
func Init() {
mdb = db.Init(table)
initIndex()
}
type WordArray []Word
func (k WordArray) Len() int {
return len(k)
}
func (k WordArray) Less(i, j int) bool {
return k[i].Count < k[j].Count
}
func (k WordArray) Swap(i, j int) {
k[i], k[j] = k[j], k[i]
}
+234
View File
@@ -0,0 +1,234 @@
package kwstatmod
import (
"fmt"
"time"
"91porn-server/common/constant"
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/common/timeutil/timerange"
"91porn-server/models"
"github.com/jinzhu/now"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var mdb *db.MongoDB
const table = models.KeywordStat
func coll(t *db.MongoTool) *db.MongoTool {
if t == nil {
return mdb.Coll(table)
}
return t.Coll(table)
}
// InitIndex 设置index
func initIndex() {
many := []mongo.IndexModel{
{
Keys: bson.D{{Key: "word", Value: 1}},
},
{
Keys: bson.D{{Key: "realm", Value: 1}, {Key: "word", Value: 1}},
},
{
Keys: bson.D{{Key: "sumDate", Value: -1}, {Key: "realm", Value: 1}},
},
{
Keys: bson.D{{Key: "sumDate", Value: -1}, {Key: "realm", Value: 1}, {Key: "word", Value: 1}},
Options: options.Index().SetUnique(true),
},
{
Keys: bson.D{{Key: "count", Value: -1}},
},
{
Keys: bson.D{{Key: "createdAt", Value: -1}},
},
{
Keys: bson.D{{Key: "updatedAt", Value: -1}},
},
}
if _, err := coll(nil).CreateIndex(many); err != nil {
panic(fmt.Sprintf("%s model set index err ==>[%+v]", table, err))
}
}
func ChangeStatTrans(trans *db.MongoTool, position time.Time, recordTime time.Time, docList []KeywordIncDoc) error {
sumDate := timerange.LocDayRange(position).Head
// create the slice of write models
writes := make([]mongo.WriteModel, len(docList))
for i, doc := range docList {
filter := M{
"sumDate": sumDate,
"realm": doc.Realm,
"word": doc.Word,
}
update := M{
"$setOnInsert": M{
"sumDate": sumDate,
"realm": doc.Realm,
"word": doc.Word,
"createdAt": time.Now(),
},
"$set": M{
"recordAt": recordTime,
"updatedAt": time.Now(),
},
"$inc": M{
"count": doc.Count,
},
}
writes[i] = mongo.NewUpdateOneModel().
SetFilter(filter).
SetUpdate(update).
SetUpsert(true)
log.Debug(fmt.Sprintf("sumDate:%s table:%s [ realm:%+v word:%+v filter:%+v update:%+v ]\n", sumDate, table, doc.Realm, doc.Word, filter, update))
}
if len(writes) == 0 {
return nil
}
//bulkWrite 不是原子操作 不具备事务性
opt := (&options.BulkWriteOptions{}).SetOrdered(false) //设为无序,触发并行写,提升写效率
_, err := coll(trans).Bulk(writes, opt)
return err
}
func list(sumDate time.Time, realms []constant.RealmType, enable bool, sort bson.D, skip, limit int64) ([]Keyword, error) {
filter := M{
"sumDate": sumDate,
"realm": M{"$in": realms},
}
realmCount := len(realms)
if realmCount == 0 {
return []Keyword{}, nil
}
if realmCount == 1 { //只有一个元素 不使用$in 提升效率
filter["realm"] = realms[0]
}
opt := (&options.FindOptions{}).
SetSort(sort).
SetSkip(skip).
SetLimit(limit)
keywords := make([]Keyword, 0, limit)
if err := coll(nil).Find(&keywords, filter, opt); err != nil {
log.ZapLog.Error(fmt.Sprintf("coll:%s list fail error:%+v:", table, err))
return nil, err
}
return keywords, nil
}
// RealmRanking RealmRanking
func RealmRanking(sumDate time.Time, realm constant.RealmType, skip int64, limit int64) ([]Keyword, error) {
sort := bson.D{
{Key: "sortKey", Value: -1},
{Key: "count", Value: -1},
}
keywords, err := list(sumDate, []constant.RealmType{realm}, true, sort, skip, limit)
if err != nil {
log.ZapLog.Error(fmt.Sprintf("coll:%s RealmRanking fail error:%+v:", table, err))
return nil, err
}
return keywords, nil
}
// KeywordCountMapBySumDate
func KeywordCountMapBySumDate(sumDate time.Time) (map[string]int64, error) {
pipeline := []M{
{
"$match": M{
"sumDate": sumDate,
},
},
{
"$group": M{
"_id": "$word",
"count": M{"$sum": "$count"},
},
},
{
"$sort": M{
"count": -1,
},
},
}
var list []struct {
Word string `bson:"_id"`
Count int64 `bson:"count"`
}
if err := coll(nil).Aggregate(&list, pipeline); err != nil {
return nil, err
}
m := make(map[string]int64, len(list))
for _, v := range list {
m[v.Word] = v.Count
}
return m, nil
}
// HotSearchWordsToday 获取今日搜索较高的关键词 视频关键字
func HotSearchWordsToday(top int) ([]string, error) {
var kw []Keyword
var words []string
opts := options.FindOptions{}
sort := bson.D{{Key: "count", Value: -1}}
opts.SetSort(sort).SetLimit(int64(top))
if err := coll(nil).Find(&kw, M{"realm": "video", "sumDate": now.BeginningOfDay()}, &opts); err != nil {
log.Error("keywordStat model HotSearchWordsToday error", log.E(err), log.Any("top", top))
return words, err
}
words = make([]string, len(kw))
for i, k := range kw {
words[i] = k.Word
}
return words, nil
}
func GetKeywordsExcludeWords(sumDate time.Time, realm string, words []string, skip int64, limit int64) (data []Keyword, err error) {
if words == nil {
words = []string{}
}
query := bson.M{"word": bson.M{"$nin": words}}
opts := options.Find()
sort := bson.D{
{Key: "sortKey", Value: -1},
{Key: "count", Value: -1},
}
opts.SetSort(sort).SetSkip(skip).SetLimit(limit)
if err = coll(nil).Find(&data, query, opts); err != nil {
log.Error("keywordStat model GetKeywordsExcludeWords error", log.E(err), log.Any("sumDate", sumDate), log.Any("words", words))
return
}
return
}
// 根据时间获取今日加昨日的关键字
func GetKeywordsBySumDate(sumDate time.Time, realm string, skip int64, limit int64) (data []Keyword, err error) {
var query = bson.M{"sumDate": bson.M{"$gte": sumDate}, "realm": realm}
var opts = options.Find()
opts.SetSort(bson.D{{Key: "sortKey", Value: -1}, {Key: "count", Value: -1}}).SetSkip(skip).SetLimit(limit)
if err = coll(nil).Find(&data, query, opts); err != nil {
log.Error("keywordStat model GetKeywordsBySumDate error", log.E(err), log.Any("sumDate", sumDate), log.Any("realm", realm))
return
}
return
}
// 根据时间段获取关键字
func GetKeywordsByTimeRange(start time.Time, end time.Time) (data []Keyword, err error) {
var query = bson.M{"updatedAt": bson.M{"$gte": start, "$lt": end}}
if err = coll(nil).Find(&data, query); err != nil {
log.Error("keywordStat model GetKeywordsByTimeRange error", log.E(err))
return
}
return
}
func DeleteBeforeSumDate(t *db.MongoTool, tm time.Time) error {
_, err := coll(t).DeleteMany(bson.M{"sumDate": bson.M{"$lt": tm}})
return err
}
+51
View File
@@ -0,0 +1,51 @@
package kwstatmod
import (
"time"
"91porn-server/common/constant"
"91porn-server/common/db"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type (
ObjectID = primitive.ObjectID
M = bson.M
KeywordSetDoc = KeywordIncDoc
)
type Keyword struct {
ID primitive.ObjectID `bson:"_id,omitempty"` // ID
SumDate time.Time `bson:"sumDate"`
Realm constant.RealmType `bson:"realm"`
Word string `bson:"word"`
Count int64 `bson:"count"`
RecordAt time.Time `bson:"recordAt"` //数据记录时间
CreatedAt time.Time `bson:"createdAt"` // 创建时间
UpdatedAt time.Time `bson:"updatedAt"` // 更新时间
}
type ESKeywordSrc struct {
ID primitive.ObjectID `json:"id"` // ID
SumDate time.Time `json:"sumDate"`
Realm constant.RealmType `json:"realm"`
Word string `json:"word"`
Count int64 `json:"count"`
RecordAt time.Time `json:"recordAt"` //数据记录时间
CreatedAt time.Time `json:"createdAt"` // 创建时间
UpdatedAt time.Time `json:"updatedAt"` // 更新时间
}
type KeywordIncDoc struct {
Realm constant.RealmType `bson:"realm,omitempty"`
Word string `bson:"word,omitempty"`
Count int64 `bson:"count,omitempty"`
}
func Init() {
mdb = db.Init(table)
initIndex()
}
+43
View File
@@ -0,0 +1,43 @@
package sectionstatmod
import (
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
)
// IncSectionHits 增加专题每日点击量
func IncSectionHits(sectionID primitive.ObjectID, date string) error {
filter := bson.M{"sectionID": sectionID, "date": date}
_, err := coll(nil).UpdateOne(filter, bson.M{"$inc": bson.M{"hitsPerDay": 1}})
return err
}
// UpsertSectionHits upsert某一天专题点击量
func UpsertSectionHits(sectionID primitive.ObjectID, date string) error {
filter := bson.M{"sectionID": sectionID, "date": date}
now := time.Now()
update := bson.M{
"$set": bson.M{"sectionID": sectionID, "date": date, "updatedAt": now},
"$setOnInsert": bson.M{"createdAt": now},
"$inc": bson.M{"hitsPerDay": 1},
}
_, err := coll(nil).UpsertOne(filter, update)
return err
}
// ListSectionHits 按页检索专题统计数据
func ListSectionHits(req ListRequest) (resp ListResponse, err error) {
opts := options.Find().SetSkip(int64(req.PageSize * (req.PageNumber - 1))).SetLimit(int64(req.PageSize + 1)).
SetSort(bson.D{{Key: "date", Value: -1}})
if err = coll(nil).Find(&resp.List, bson.M{"sectionID": req.SectionID}, opts); err != nil {
return
}
if uint64(len(resp.List)) > req.PageSize {
resp.HasNext = true
resp.List = resp.List[:req.PageSize]
}
return
}
+63
View File
@@ -0,0 +1,63 @@
package sectionstatmod
import (
"fmt"
"time"
"91porn-server/common/db"
"91porn-server/models"
"91porn-server/models/commod"
"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"
)
type SectionStat struct {
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"` // id
SectionID primitive.ObjectID `json:"sectionID" bson:"sectionID"` // 专题ID
HitsPerday int64 `json:"hitsPerDay" bson:"hitsPerDay"` // 每日点击量
Date string `json:"date" bson:"date"` // 统计日期
CreatedAt time.Time `json:"createdAt" bson:"createdAt,omitempty"` // 创建时间
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt,omitempty"` // 刷新时间
}
var mdb *db.MongoDB
const table = models.SectionClick
func coll(t *db.MongoTool) *db.MongoTool {
if t == nil {
return mdb.Coll(table)
}
return t.Coll(table)
}
func Init() {
mdb = db.Init(table)
initIndex()
}
// initIndex 初始化索引
func initIndex() {
many := []mongo.IndexModel{
{
Keys: bson.D{{Key: "sectionID", Value: 1}, {Key: "date", Value: 1}},
Options: options.Index().SetUnique(true),
},
}
if _, err := coll(nil).CreateIndex(many); err != nil {
panic(fmt.Sprintf("%s model set index err ==>[%+v]", table, err))
}
}
type ListRequest struct {
SectionID primitive.ObjectID `json:"sectionID" swaggertype:"string" binding:"required"` // 专题ID
commod.Page
}
type ListResponse struct {
List []SectionStat // 统计列表
HasNext bool // 下一页
}
+95
View File
@@ -0,0 +1,95 @@
package statordermod
import (
"time"
"91porn-server/common/log"
"91porn-server/common/pageopt"
"go.mongodb.org/mongo-driver/mongo/options"
)
type (
Matcher = pageopt.Matcher
// ChannelMatch
ChannelMatch struct {
Type *string
}
)
func (t *ChannelMatch) New() Matcher {
return pageopt.NewAssignMatch("channel", t.Type)
}
// TypeMatch
type TypeMatch struct {
Type *OrderType
}
func (t *TypeMatch) New() Matcher {
return pageopt.NewAssignMatch("type", t.Type)
}
// SumDateGTEAndLTMatch
type SumDateGTEAndLTMatch struct {
GTE *time.Time
LT *time.Time
}
func (s *SumDateGTEAndLTMatch) New() Matcher {
return pageopt.NewGTEAndLTMatch("sumDate", s.GTE, s.LT)
}
var Sort_SumDate_n1 = Sort{{Key: "sumDate", Value: -1}}
func List(sort Sort, skip, limit *int64, matchers ...Matcher) ([]OrderState, error) {
filter := pageopt.MergeM(matchers)
opt := (&options.FindOptions{})
if len(sort) != 0 {
opt.SetSort(sort)
}
if skip != nil {
opt.SetSkip(*skip)
}
if limit != nil {
opt.SetLimit(*limit)
}
list := []OrderState{}
if err := coll(nil).Find(&list, filter, opt); err != nil {
log.Error("statordermod List error", log.E(err))
return nil, err
}
return list, nil
}
func Count(matchers ...Matcher) (int64, error) {
filter := pageopt.MergeM(matchers)
count, err := coll(nil).Count(filter)
if err != nil {
log.Error("statordermod Count error", log.E(err))
return 0, err
}
return count, nil
}
// Sum
func Sum(matchers ...Matcher) (Items, error) {
filter := pageopt.MergeM(matchers)
pipeline := []M{}
pipeline = append(pipeline, M{"$match": filter})
pipeline = append(pipeline, M{"$group": M{"_id": nil,
"orderCount": M{"$sum": "$orderCount"},
"completeOrderCount": M{"$sum": "$completeOrderCount"},
"orderAmount": M{"$sum": "$orderAmount"},
"completeOrderAmount": M{"$sum": "$completeOrderAmount"},
"paidOrderCount": M{"$sum": "$paidOrderCount"},
"paidAmount": M{"$sum": "$paidAmount"},
}})
items := Items{}
if err := coll(nil).AggregateDecode(&items, pipeline); err != nil {
log.Error("statordermod Sum error", log.E(err))
return Items{}, err
}
return items, nil
}
+123
View File
@@ -0,0 +1,123 @@
package statordermod
import (
"fmt"
"time"
"91porn-server/common"
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/models"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var mdb *db.MongoDB
const table = models.OrderStat
func coll(t *db.MongoTool) *db.MongoTool {
if t == nil {
return mdb.Coll(table)
}
return t.Coll(table)
}
// initIndex 初始化索引
func initIndex() {
many := []mongo.IndexModel{
{
Keys: bson.D{{Key: "sumDate", Value: -1}},
},
{
Keys: bson.D{{Key: "channel", Value: 1}, {Key: "type", Value: 1}},
},
{
Keys: bson.D{{Key: "sumDate", Value: -1}, {Key: "channel", Value: 1}, {Key: "type", Value: 1}},
Options: options.Index().SetUnique(true),
},
}
if _, err := coll(nil).CreateIndex(many); err != nil {
panic(fmt.Sprintf("%s model set index err ==>[%+v]", table, err))
}
}
func ChangeRechargeStatTrans(trans *db.MongoTool, sumDate, recordTime time.Time, stateMap RecOrderStateMap) error {
writes := make([]mongo.WriteModel, len(stateMap))
i := 0
for channel, statDoc := range stateMap {
model, err := toMgoModel(sumDate, recordTime, channel, Recharge, statDoc.RechargeOrderStateIncDoc, statDoc.RechargeOrderStateSetDoc)
if err != nil {
return err
}
writes[i] = model
i++
}
if len(writes) == 0 {
return nil
}
//bulkWrite 不是原子操作 不具备事务性
opt := (&options.BulkWriteOptions{}).SetOrdered(false) //设为无序,触发并行写,提升写效率
_, err := coll(trans).Bulk(writes, opt)
return err
}
func ChangeWithDrawStatTrans(trans *db.MongoTool, sumDate, recordTime time.Time, stateMap WitOrderStateMap) error {
writes := make([]mongo.WriteModel, len(stateMap))
i := 0
for channel, statDoc := range stateMap {
model, err := toMgoModel(sumDate, recordTime, channel, WithDraw, statDoc.WithDrawOrderStateIncDoc, statDoc.WithDrawOrderStateSetDoc)
if err != nil {
return err
}
writes[i] = model
i++
}
if len(writes) == 0 {
return nil
}
//bulkWrite 不是原子操作 不具备事务性
opt := (&options.BulkWriteOptions{}).SetOrdered(false) //设为无序,触发并行写,提升写效率
_, err := coll(trans).Bulk(writes, opt)
return err
}
func toMgoModel(sumDate, recordTime time.Time, channel string, typ OrderType, inc orderStateIncDoc, set orderStateSetDoc) (mongo.WriteModel, error) {
filter := M{
"sumDate": sumDate,
"channel": channel,
"type": typ,
}
setM, err := common.ToBsonM(set)
if err != nil {
return nil, err
}
setM["recordAt"] = recordTime
setM["updatedAt"] = time.Now()
incM, err := common.ToBsonM(inc)
if err != nil {
return nil, err
}
update := M{
"$setOnInsert": M{
"sumDate": sumDate,
"channel": channel,
"type": typ,
"createdAt": time.Now(),
},
}
if len(incM) != 0 {
update["$inc"] = incM
}
if len(setM) != 0 {
update["$set"] = setM
}
model := mongo.NewUpdateOneModel().
SetFilter(filter).
SetUpdate(update).
SetUpsert(true)
log.Debug(fmt.Sprintf("sumDate:%s table:%s [ typ:%s channel:%s filter:%+v update:%+v ]\n", sumDate, table, typ.String(), channel, filter, update))
return model, nil
}
+90
View File
@@ -0,0 +1,90 @@
package statordermod
import (
"time"
"91porn-server/common/db"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type (
OrderType int
M = bson.M
Sort = bson.D
)
const (
Recharge OrderType = 1
WithDraw OrderType = 2
)
func (o *OrderType) String() string {
switch *o {
case Recharge:
return "recharge"
case WithDraw:
return "withDraw"
default:
return "unknow"
}
}
type Items struct {
OrderCount int64 `json:"orderCount" bson:"orderCount"` //生产订单数
CompleteOrderCount int64 `json:"completeOrderCount" bson:"completeOrderCount"` //完成订单数
OrderAmount int64 `json:"orderAmount" bson:"orderAmount"` //生产订单金额
CompleteOrderAmount int64 `json:"completeOrderAmount" bson:"completeOrderAmount"` //完成订单的实际支付金额
PaidOrderCount int64 `json:"paidOrderCount" bson:"paidOrderCount"` //用户已支付金额/三方已出款金额
PaidAmount int64 `json:"paidAmount" bson:"paidAmount"` //用户已支付订单数/三方已出款订单数
}
// OrderState 充值提现统计
type OrderState struct {
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"` // ID
SumDate time.Time `json:"sumDate" bson:"sumDate"`
Items `bson:",inline"`
Channel string `json:"channel" bson:"channel"` //充值渠道
Type OrderType `json:"type" bson:"type"` //订单类型, 1,充值;2,提现
RecordAt time.Time `json:"recordAt" bson:"recordAt"` //数据记录时间
CreatedAt time.Time `json:"createdAt" bson:"createdAt"` //文档创建时间
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"` //文档更新时间
}
func Init() {
mdb = db.Init(table)
initIndex()
}
type orderStateSetDoc struct {
}
type orderStateIncDoc struct {
OrderCount *int64 `bson:"orderCount,omitempty"` //订单数
CompleteOrderCount *int64 `bson:"completeOrderCount,omitempty"` //完成订单数
OrderAmount *int64 `bson:"orderAmount,omitempty"` //订单金额
CompleteOrderAmount *int64 `bson:"completeOrderAmount,omitempty"` //实际支付金额
PaidOrderCount *int64 `bson:"paidOrderCount,omitempty"` //用户已支付订单数/三方已出款订单数
PaidAmount *int64 `bson:"paidAmount,omitempty"` //用户已支付金额/三方已出款金额
}
type RechargeOrderStateIncDoc = orderStateIncDoc
type RechargeOrderStateSetDoc = orderStateSetDoc
type RechargeOrderStateDoc struct {
RechargeOrderStateIncDoc
RechargeOrderStateSetDoc
}
type RecOrderStateMap = map[string]*RechargeOrderStateDoc
type WithDrawOrderStateIncDoc = orderStateIncDoc
type WithDrawOrderStateSetDoc = orderStateSetDoc
type WithDrawOrderStateDoc struct {
WithDrawOrderStateIncDoc
WithDrawOrderStateSetDoc
}
type WitOrderStateMap = map[string]*WithDrawOrderStateDoc
+182
View File
@@ -0,0 +1,182 @@
package statrecordmod
import (
"fmt"
"time"
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/models"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var mdb *db.MongoDB
const table = models.StatRecord
func coll(t *db.MongoTool) *db.MongoTool {
if t == nil {
return mdb.Coll(table)
}
return t.Coll(table)
}
// initIndex 初始化索引
func initIndex() {
many := []mongo.IndexModel{
{
Keys: bson.D{{Key: "name", Value: 1}},
Options: options.Index().SetUnique(true),
},
{
Keys: bson.D{{Key: "job", Value: 1}, {Key: "item", Value: 1}},
Options: options.Index().SetSparse(true).SetUnique(true),
},
{
Keys: bson.D{{Key: "createdAt", Value: -1}},
},
{
Keys: bson.D{{Key: "updatedAt", Value: -1}},
},
}
if _, err := coll(nil).CreateIndex(many); err != nil {
panic(fmt.Sprintf("%s model set index err ==>[%+v]", table, err))
}
}
// ChangeStatTrans ChangeStatTrans
func UpsertOneTrans(trans *db.MongoTool, name string, recordTime time.Time) error {
filter := bson.M{
"name": name,
}
update := bson.M{
"$set": bson.M{
"recordTime": recordTime,
"updatedAt": time.Now(),
},
"$setOnInsert": bson.M{
"name": name,
"fristRecordTime": recordTime,
"createdAt": time.Now(),
},
}
if _, err := coll(trans).UpsertOne(filter, update); err != nil {
log.Error("statrecordmod UpsertOneTrans error:%v\n", log.E(err))
return err
}
return nil
}
type ItemRecordAt struct {
Job string
Item string
RecordTime time.Time
}
func MergeName(job string, item string) string {
return fmt.Sprintf("%s.%s", job, item)
}
func UpsertOneItemTrans(trans *db.MongoTool, job string, item string, recordTime time.Time) error {
filter := bson.M{
"job": job,
"item": item,
}
update := bson.M{
"$set": bson.M{
"recordTime": recordTime,
"updatedAt": time.Now(),
},
"$setOnInsert": bson.M{
"name": MergeName(job, item),
"job": job,
"item": item,
"fristRecordTime": recordTime,
"createdAt": time.Now(),
},
}
if _, err := coll(trans).UpsertOne(filter, update); err != nil {
log.Error("statrecordmod UpsertOneItemTrans error:%v\n", log.E(err))
return err
}
return nil
}
// FindOne FindOne
func findOneByName(Name string) (Record, error) {
filter := bson.M{
"name": Name,
}
doc := Record{}
return doc, coll(nil).FindOne(&doc, filter)
}
// LastRecordTime 最后的记录时间,服务器的时区
func LastRecordTime(name string, defaultTime time.Time) (time.Time, error) {
//获取上次记录时间
doc, err := findOneByName(name)
if err != nil {
return time.Time{}, fmt.Errorf("LastRecordTime err:%+v\n", err)
}
//设定为服务器的时区
var tim = defaultTime
if !doc.RecordTime.IsZero() {
tim = doc.RecordTime
}
//不依赖数据库 保证时间是服务器时间
localTime := tim.In(time.Local)
log.Debug("LastRecordTime local Info", log.Any("Local", time.Local), log.Any("time", tim), log.Any("returnTime", localTime))
return localTime, nil
}
func FristRecordTimeByName(name string) (time.Time, error) {
doc, err := findOneByName(name)
if err != nil {
return time.Time{}, fmt.Errorf("FristRecordTimeByName err:%+v\n", err)
}
return doc.FristRecordTime, nil
}
func findOneByJob(job string, item string) (Record, error) {
filter := bson.M{
"job": job,
"item": item,
}
doc := Record{}
return doc, coll(nil).FindOne(&doc, filter)
}
func FristRecordTimeByJob(job string, item string) (time.Time, error) {
doc, err := findOneByJob(job, item)
if err != nil {
return time.Time{}, fmt.Errorf("FristRecordTimeByName err:%+v\n", err)
}
return doc.FristRecordTime, nil
}
func findManyByJob(job string) ([]Record, error) {
filter := bson.M{
"job": job,
}
list := []Record{}
return list, coll(nil).Find(&list, filter)
}
func LastRecordTimeMap(job string, defaultTimeMap map[string]time.Time) (map[string]time.Time, error) {
list, err := findManyByJob(job)
if err != nil {
return nil, err
}
for _, v := range list {
defaultTimeMap[v.Item] = v.RecordTime
}
return defaultTimeMap, nil
}
func DeleteStatRecordByName(name string) error {
_, err := coll(nil).DeleteOne(bson.M{"name": name})
return err
}
+84
View File
@@ -0,0 +1,84 @@
package statrecordmod
import (
"time"
"91porn-server/common/db"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type Record struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
Name string `bson:"name"` //任务名
Job string `bson:"job"` //工作
Item string `bson:"item"` //项目
RecordTime time.Time `bson:"recordTime"` //提交时间
FristRecordTime time.Time `bson:"fristRecordTime"` //第一次提交时间
CreatedAt time.Time `bson:"createdAt"` //创建时间
UpdatedAt time.Time `bson:"updatedAt"` //更新时间
}
const (
OneMinuteScale = 1 //对齐时间(分钟)
FiveMinuteScale = 5 //对齐时间(分钟)
FifteenMinuteScale = 15 //对齐时间(分钟)
ThirtyMinuteScale = 30 //对齐时间(分钟)
OneDayScale = 1440 //对齐时间(分钟)
)
const (
//stat 基本统计
DailyStatJob1 = "dailyStatJob1"
ChannelStatJob = "channelStatJob"
VideoStatJob = "videoStatJob"
VideoTotalStatJob = "videoTotalStatJob"
ChannelRetainStatJob = "channelRetainStatJob"
KeywordStatJob = "keywordStatJob"
KeywordRankStatJob = "keywordRankStatJob"
RechargeOrderStatJob = "rechargeOrderStatJob"
WithdrawOrderStatJob = "withdrawOrderStatJob"
TagPlayStatJob = "tagPlayStatJob"
ADSStatJob = "adsStatJob"
TotalJob = "totalJob"
ChTotalJob = "chTotalJob"
InvitStatJob = "InvitStatJob"
DataPointJob = "dataPointJob"
UserStatJob = "userStatJob"
DiscStatJob = "discStatJob"
DiscTotalStatJob = "discTotalStatJob"
Retain1StatJob = "retainStatJob1"
DicsZoomStatJob = "dicsZoomStatJob"
//act 活动统计,生命周期等于活动时间
ActInvitCountJob_01_17 = "ActInvitCountJob_01_17"
ActInvitCoinsJob_01_17 = "ActInvitCoinsJob_01_17"
LotteryActID = "LotteryActID"
// 数据同步
ElasticSyncVideoJob = "esVideoJob"
ElasticSyncMediaJob = "esMediaJob"
ElasticSyncTagJob = "esTagJob"
ElasticSyncUserJob = "esUserJob"
ElasticSyncKeywordJob = "esKeywordJob"
ElasticSyncSearchLogJob = "esSearchLogJob"
ElasticSyncLoufengJob = "esLouFengJob"
ElasticSyncAudioBookJob = "esAudioBookJob"
SpiderSyncLoufengJob = "spiderLouFengJob"
SpiderSyncfictionJob = "spiderFictionJob"
SpiderSyncAudiobookJob = "spiderAudiobookJob"
SpiderSyncLoufengAgentJob = "spiderSyncLoufengAgentJob"
RedisSyncShortVideoJob = "rdShortVideoJob"
//sql折叠
SqlCollasperVidPageView = "sqlCollasperVidPageView"
SqlCollasperUserTotalWork = "sqlCollasperUserTotalWorks"
)
func Init() {
mdb = db.Init(table)
initIndex()
}
+90
View File
@@ -0,0 +1,90 @@
package stattagplaymod
import (
"fmt"
"time"
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/models"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var mdb *db.MongoDB
const table = models.TagPlayStat
func coll(t *db.MongoTool) *db.MongoTool {
if t == nil {
return mdb.Coll(table)
}
return t.Coll(table)
}
func initIndex() {
many := []mongo.IndexModel{
{
Keys: bson.D{{Key: "tagID", Value: 1}, {Key: "videoID", Value: 1}},
Options: options.Index().SetUnique(true),
},
{
Keys: bson.D{{Key: "playCount", Value: 1}, {Key: "updatedAt", Value: 1}},
},
{
Keys: bson.D{{Key: "updatedAt", Value: -1}},
},
}
if _, err := coll(nil).CreateIndex(many); err != nil {
panic(fmt.Sprintf("%s model set index err ==>[%+v]", table, err))
}
}
func GetTagPlayCount(tagID ObjectID, videoID ObjectID) (TagPlay, error) {
tagPlay := TagPlay{}
if err := coll(nil).FindOne(&tagPlay, bson.M{"tagID": tagID, "videoID": videoID}); err != nil {
log.Error("stat tag play model GetTagPlayCount error", log.E(err), log.Any("tagID", tagID), log.Any("videoID", videoID))
return tagPlay, err
}
return tagPlay, nil
}
func ChangeStatTrans(trans *db.MongoTool, incDoc []IncDoc) error {
incDocLen := len(incDoc)
if incDocLen == 0 {
return nil
}
// create the slice of write models
writes := make([]mongo.WriteModel, incDocLen)
for i, tagPlay := range incDoc {
filter := bson.M{
"tagID": tagPlay.TagID,
"videoID": tagPlay.VideoID,
}
update := bson.M{
"$setOnInsert": bson.M{
"tagID": tagPlay.TagID,
"videoID": tagPlay.VideoID,
},
"$inc": bson.M{
"playCount": tagPlay.PlayCount,
},
"$set": bson.M{
"updatedAt": time.Now(),
},
}
writes[i] = mongo.NewUpdateOneModel().
SetFilter(filter).
SetUpdate(update).
SetUpsert(true)
}
//bulkWrite 不是原子操作 不具备事务性
opt := (&options.BulkWriteOptions{}).SetOrdered(false) //设为无序,触发并行写,提升写效率
if _, err := coll(trans).Bulk(writes, opt); err != nil {
log.Warn("stat tag play model Bulk error", log.E(err))
return err
}
return nil
}
+30
View File
@@ -0,0 +1,30 @@
package stattagplaymod
import (
"time"
"91porn-server/common/db"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type ObjectID = primitive.ObjectID
type TagPlay struct {
ID ObjectID `bson:"_id,omitempty"`
TagID ObjectID `bson:"tagID"`
VideoID ObjectID `bson:"videoID"`
PlayCount int64 `bson:"playCount"`
UpdatedAt time.Time `bson:"updatedAt"`
}
type IncDoc struct {
TagID ObjectID `bson:"tagID"`
VideoID ObjectID `bson:"videoID"`
PlayCount int64 `bson:"playCount"`
}
func Init() {
mdb = db.Init(table)
initIndex()
}
+31
View File
@@ -0,0 +1,31 @@
package statusermod
import (
"91porn-server/common/pageopt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
type Matcher = pageopt.Matcher
// ItemMatch
type ItemMatch struct {
Item Itemtype
}
func (t *ItemMatch) New() Matcher {
return pageopt.NewAssignMatch("item", t.Item)
}
func List(sort bson.D, skip, limit int64, matchers ...Matcher) ([]UserStat, error) {
filter := pageopt.MergeM(matchers)
opt := &options.FindOptions{}
if len(sort) != 0 {
opt.SetSort(sort)
}
opt.SetSkip(skip)
opt.SetLimit(limit)
list := make([]UserStat, 0, limit)
return list, coll(nil).Find(&list, filter, opt)
}
+47
View File
@@ -0,0 +1,47 @@
package statusermod
import (
"time"
"91porn-server/common/db"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type (
M = bson.M
D = bson.D
Itemtype string
)
const (
VidIncome Itemtype = "vidIncome" //视频收益
UploadCount Itemtype = "uploadCount" //上传数量
)
type UserStat struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
UID uint64 `bson:"uid"`
Item Itemtype `bson:"item"`
Count int64 `bson:"count"`
RecordAt time.Time `bson:"recordAt"` //数据记录时间
UpdatedAt time.Time `bson:"updatedAt"` //文档更新时间
CreatedAt time.Time `bson:"createdAt"` //文档创建时间
}
type SetDoc struct {
UID uint64 `bson:"uid"`
Item Itemtype `bson:"item"`
Count int64 `bson:"count"`
RecordAt time.Time `bson:"recordAt"` //数据记录时间
}
type IncDoc SetDoc
func Init() {
mdb = db.Init(table)
initIndex()
}
+141
View File
@@ -0,0 +1,141 @@
package statusermod
import (
"fmt"
"time"
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/models"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var mdb *db.MongoDB
const table = models.UserStat
func coll(t *db.MongoTool) *db.MongoTool {
if t == nil {
return mdb.Coll(table)
}
return t.Coll(table)
}
// initIndex 初始化索引
func initIndex() {
many := []mongo.IndexModel{
{
Keys: bson.D{{Key: "uid", Value: 1}, {Key: "item", Value: 1}},
Options: options.Index().SetUnique(true),
},
{
Keys: bson.D{{Key: "uid", Value: -1}},
},
{
Keys: bson.D{{Key: "item", Value: -1}},
},
{
Keys: bson.D{{Key: "count", Value: -1}},
},
{
Keys: bson.D{{Key: "recordAt", Value: -1}},
},
{
Keys: bson.D{{Key: "updatedAt", Value: -1}},
},
{
Keys: bson.D{{Key: "createdAt", Value: -1}},
},
}
if _, err := coll(nil).CreateIndex(many); err != nil {
panic(fmt.Sprintf("%s model set index err ==>[%+v]", table, err))
}
}
func ChangeIncStatTrans(trans *db.MongoTool, incDocList []IncDoc) error {
// create the slice of write models
writes := make([]mongo.WriteModel, len(incDocList))
for i, incDoc := range incDocList {
filter := M{
"uid": incDoc.UID,
"item": incDoc.Item,
}
update := M{
"$setOnInsert": M{
"uid": incDoc.UID,
"item": incDoc.Item,
"createdAt": time.Now(),
},
"$inc": M{
"count": incDoc.Count, //设计为统计表keywordStat聚合word得到
},
"$set": M{
"updatedAt": time.Now(),
"recordAt": incDoc.RecordAt,
},
}
writes[i] = mongo.NewUpdateOneModel().
SetFilter(filter).
SetUpdate(update).
SetUpsert(true)
log.Debug(fmt.Sprintf("table:%s [ filter:%+v update:%+v ]\n", table, filter, update))
}
if len(writes) == 0 {
return nil
}
//bulkWrite 不是原子操作 不具备事务性
opt := (&options.BulkWriteOptions{}).SetOrdered(false) //设为无序,触发并行写,提升写效率
_, err := coll(trans).Bulk(writes, opt)
return err
}
func ChangeSetStatTrans(trans *db.MongoTool, setDocList []SetDoc) error {
// create the slice of write models
writes := make([]mongo.WriteModel, len(setDocList))
for i, setDoc := range setDocList {
filter := M{
"uid": setDoc.UID,
"item": setDoc.Item,
}
update := M{
"$setOnInsert": M{
"uid": setDoc.UID,
"item": setDoc.Item,
"createdAt": time.Now(),
},
"$set": M{
"count": setDoc.Count,
"updatedAt": time.Now(),
"recordAt": setDoc.RecordAt,
},
}
writes[i] = mongo.NewUpdateOneModel().
SetFilter(filter).
SetUpdate(update).
SetUpsert(true)
log.Debug(fmt.Sprintf("table:%s [ filter:%+v update:%+v ]\n", table, filter, update))
}
if len(writes) == 0 {
return nil
}
//bulkWrite 不是原子操作 不具备事务性
opt := (&options.BulkWriteOptions{}).SetOrdered(false) //设为无序,触发并行写,提升写效率
_, err := coll(trans).Bulk(writes, opt)
return err
}
func ListByVidIncome(limit int64) ([]UserStat, error) {
sort := bson.D{{Key: "count", Value: -1}}
itemMatch := ItemMatch{VidIncome}
return List(sort, 0, limit, itemMatch.New())
}
func ListByUploadCount(limit int64) ([]UserStat, error) {
sort := bson.D{{Key: "count", Value: -1}}
itemMatch := ItemMatch{UploadCount}
return List(sort, 0, limit, itemMatch.New())
}
+93
View File
@@ -0,0 +1,93 @@
package statvidmod
import (
"time"
"91porn-server/common/pageopt"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
type Matcher = pageopt.Matcher
// SumDateMatch
type SumDateMatch struct {
SumDate *time.Time
}
func (s *SumDateMatch) New() Matcher {
return pageopt.NewAssignMatch("sumDate", s.SumDate)
}
// SumDateGTEAndLTMatch
type SumDateGTEAndLTMatch struct {
GTE *time.Time
LT *time.Time
}
func (s *SumDateGTEAndLTMatch) New() Matcher {
return pageopt.NewGTEAndLTMatch("sumDate", s.GTE, s.LT)
}
// PublisherIDMatch
type PublisherIDMatch struct {
UID *uint64
}
func (p *PublisherIDMatch) New() Matcher {
return pageopt.NewAssignMatch("publisherID", p.UID)
}
// PayCountGTMatch
type PayCountGTMatch struct {
GT *int64
}
func (p *PayCountGTMatch) New() Matcher {
return pageopt.NewGTMatch("payCount", p.GT)
}
// PlayCountGTMatch
type PlayCountGTMatch struct {
GT *int64
}
func (p *PlayCountGTMatch) New() Matcher {
return pageopt.NewGTMatch("playCount", p.GT)
}
// VidMatch
type VidMatch struct {
Vid *ObjectID
}
func (p *VidMatch) New() Matcher {
return pageopt.NewAssignMatch("vid", p.Vid)
}
// VidInMatch
type VidInMatch struct {
Vids []ObjectID
}
func (p *VidInMatch) New() Matcher {
return pageopt.NewInMatch("vid", p.Vids)
}
func List(sort bson.D, skip, limit int64, matchs ...Matcher) ([]Stat, error) {
opt := &options.FindOptions{}
if len(sort) != 0 {
opt.SetSort(sort)
}
opt.SetSkip(skip)
opt.SetLimit(limit)
filter := pageopt.MergeM(matchs)
list := make([]Stat, 0, limit)
return list, coll(nil).Find(&list, filter, opt)
}
func Count(matchs ...Matcher) (int64, error) {
filter := pageopt.MergeM(matchs)
return coll(nil).Count(filter)
}
+128
View File
@@ -0,0 +1,128 @@
package statvidmod
import (
"fmt"
"time"
"91porn-server/common/log"
"91porn-server/common/maths"
"91porn-server/models/v/usermod"
"91porn-server/models/v/vidmod"
"91porn-server/web/vidhelp"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type Video struct {
ID *primitive.ObjectID `json:"id" bson:"_id,omitempty"`
Title *string `json:"title" bson:"title,omitempty"` //视频标题
PublisherID *uint64 `json:"publisherID" bson:"publisherID,omitempty"` //上传者ID
PublisherName *string `json:"publisherName"` //上传者姓名
Day *float64 `json:"day"` //售卖天数
Coins *int64 `json:"coins" bson:"coins,omitempty"` //定价
}
type VideoStatRecord struct {
SumDate *time.Time `json:"sumDate" bson:"sumDate,omitempty"`
Video Video `json:"video" bson:"video"` //视屏
Income *int64 `json:"income" bson:"income,omitempty"` //当日收入
Tax *float64 `json:"tax" bson:"tax,omitempty"` //税
PayCount *int64 `json:"payCount" bson:"payCount,omitempty"` //购买数
CreatedAt *time.Time `json:"createdAt" bson:"createdAt,omitempty"` //日志创建时间
}
type VideoStatExport struct {
SumDate time.Time `json:"sumDate" bson:"sumDate,omitempty"`
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"`
Title string `json:"title" bson:"title,omitempty"` //视频标题
PublisherID uint64 `json:"publisherID" bson:"publisherID,omitempty"` //上传者ID
Coins int64 `json:"coins" bson:"coins,omitempty"` //定价
Income int64 `json:"income" bson:"income,omitempty"` //当日收入
Tax float64 `json:"tax" bson:"tax,omitempty"` //税
PayCount int64 `json:"payCount" bson:"payCount,omitempty"` //购买数
CreatedAt time.Time `json:"createdAt" bson:"createdAt,omitempty"` //日志创建时间
}
type VideoStatPage struct {
Total int64 `json:"total" bson:"total"`
List []VideoStatRecord `json:"list" bson:"list"`
}
var payCountMin int64 = 0
// VideoStatPages
func VideoStatPages(skip, limit int64,
sumDateGTEAndLTMatch SumDateGTEAndLTMatch,
vidMatch VidMatch,
vidInMatch VidInMatch,
) (VideoStatPage, error) {
matList := []Matcher{
(&PayCountGTMatch{&payCountMin}).New(),
sumDateGTEAndLTMatch.New(),
vidMatch.New(),
vidInMatch.New(),
}
sort := bson.D{{Key: "sumDate", Value: -1}}
statList, err := List(sort, skip, limit, matList...)
if err != nil {
return VideoStatPage{}, err
}
vidList := make([]ObjectID, len(statList))
for i, v := range statList {
vidList[i] = v.Vid
}
videoMap, err := vidmod.VideoMap(vidList)
if err != nil {
return VideoStatPage{}, err
}
//用户id
statListLen := len(statList)
userIds := make([]uint64, 0, statListLen)
recordList := make([]VideoStatRecord, statListLen)
for i, stat := range statList {
_stat := stat
vid := _stat.Vid
videoInfo := Video{
ID: &vid,
}
if video, ok := videoMap[vid]; ok {
videoInfo.Title = &video.Title
videoInfo.PublisherID = &video.PublisherID
videoInfo.Coins = &video.Coins
d := vidhelp.CalculateVideoSellDays(video.ReviewAt)
videoInfo.Day = &d
userIds = append(userIds, video.PublisherID)
}
tax := maths.ToFloat64_b2(_stat.Tax)
recordList[i] = VideoStatRecord{
SumDate: &_stat.SumDate,
Video: videoInfo,
Income: &_stat.Income,
Tax: &tax,
PayCount: &_stat.PayCount,
CreatedAt: &_stat.CreatedAt,
}
}
//查询用户信息
userMap, err := usermod.UserMap(userIds)
if err == nil {
for _, r := range recordList {
if r.Video.PublisherID == nil {
log.Warn(fmt.Sprintf("视频[%v]信息异常==>缺少上传者ID", r.Video.ID))
continue
}
if u, ok := userMap[*r.Video.PublisherID]; ok {
r.Video.PublisherName = &u.Name
}
}
}
count, err := Count(matList...)
if err != nil {
return VideoStatPage{}, err
}
return VideoStatPage{
Total: count,
List: recordList,
}, nil
}
+134
View File
@@ -0,0 +1,134 @@
package statvidmod
import (
"fmt"
"time"
"91porn-server/common"
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/models"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var mdb *db.MongoDB
const table = models.VideoStat
func coll(t *db.MongoTool) *db.MongoTool {
if t == nil {
return mdb.Coll(table)
}
return t.Coll(table)
}
// initIndex 初始化索引
func initIndex() {
many := []mongo.IndexModel{
{
Keys: bson.D{{Key: "vid", Value: 1}},
},
{
Keys: bson.D{{Key: "sumDate", Value: -1}, {Key: "vid", Value: 1}},
Options: options.Index().SetUnique(true),
},
{
Keys: bson.D{{Key: "playCount", Value: -1}},
Options: options.Index().SetSparse(true),
},
{
Keys: bson.D{{Key: "createdAt", Value: -1}},
},
{
Keys: bson.D{{Key: "updatedAt", Value: -1}},
},
{
Keys: bson.D{{Key: "recordAt", Value: -1}},
},
{
Keys: bson.D{{Key: "sumDate", Value: -1}, {Key: "playCount", Value: -1}},
},
}
if _, err := coll(nil).CreateIndex(many); err != nil {
panic(fmt.Sprintf("%s model set index err ==>[%+v]", table, err))
}
}
func ChangeStatTrans(trans *db.MongoTool, sumDate time.Time, recordTime time.Time, statDocMap VideoStatDocMap) error {
writes := make([]mongo.WriteModel, len(statDocMap))
i := 0
for vid, statDoc := range statDocMap {
filter := M{
"sumDate": sumDate,
"vid": vid,
}
setM, err := common.ToBsonM(statDoc.VideoStatSetDoc)
if err != nil {
return err
}
setM["recordAt"] = recordTime
setM["updatedAt"] = time.Now()
incM, err := common.ToBsonM(statDoc.VideoStatIncDoc)
if err != nil {
return err
}
update := M{
"$setOnInsert": M{
"sumDate": sumDate,
"vid": vid,
"createdAt": time.Now(),
},
}
if len(incM) != 0 {
update["$inc"] = incM
}
if len(setM) != 0 {
update["$set"] = setM
}
writes[i] = mongo.NewUpdateOneModel().
SetFilter(filter).
SetUpdate(update).
SetUpsert(true)
i++
log.Debug(fmt.Sprintf("sumDate:%s table:%s [ vid:%+v filter:%+v update:%+v ]\n", sumDate, table, vid, filter, update))
}
if len(writes) == 0 {
return nil
}
//bulkWrite 不是原子操作 不具备事务性
opt := (&options.BulkWriteOptions{}).SetOrdered(false) //设为无序,触发并行写,提升写效率
_, err := coll(trans).Bulk(writes, opt)
return err
}
// GetDailyPlayCountGroup
// @return vid List, vid->dailyPlayCount Map, error
func GetDailyPlayCountGroup(skip, limit int64) ([]ObjectID, map[ObjectID]int64, error) {
playCountMin := int64(0)
playCountGTMatch := PlayCountGTMatch{&playCountMin}
sort := D{{Key: "sumDate", Value: -1}, {Key: "playCount", Value: -1}}
list, err := List(sort, skip, limit, playCountGTMatch.New())
if err != nil {
return nil, nil, err
}
listLen := len(list)
vidList := make([]ObjectID, listLen)
countMap := make(map[ObjectID]int64, listLen)
for i, v := range list {
vidList[i] = v.Vid
countMap[v.Vid] = v.PlayCount
}
return vidList, countMap, nil
}
func SumByRecordAt(match RecordAtGTEAndLTMatch) ([]SumGroup, error) {
return Sum(match.New())
}
func DeleteBeforeCreatedAt(t *db.MongoTool, tm time.Time) error {
_, err := coll(t).DeleteMany(bson.M{"createdAt": bson.M{"$lt": tm}})
return err
}
+44
View File
@@ -0,0 +1,44 @@
package statvidmod
import (
"time"
"91porn-server/common/pageopt"
)
type SumGroup struct {
Vid ObjectID `bson:"_id"`
Income int64 `bson:"income"` //收入
PayCount int64 `bson:"payCount"` //购买次数
PlayCount int64 `bson:"playCount"` //播放次数
PayPlayCount int64 `bson:"payPlayCount"` //付费播放次数
Tax float64 `bson:"tax"` //[未完成]税
}
// PlayCountGTMatch
type RecordAtGTEAndLTMatch struct {
GTE *time.Time
LT *time.Time
}
func (r *RecordAtGTEAndLTMatch) New() Matcher {
return pageopt.NewGTEAndLTMatch("recordAt", r.GTE, r.LT)
}
func Sum(matchs ...Matcher) ([]SumGroup, error) {
filter := pageopt.MergeM(matchs)
pipeline := []M{}
pipeline = append(pipeline, M{"$match": filter})
pipeline = append(pipeline, M{
"$group": M{
"_id": "$vid",
"income": M{"$sum": "$income"},
"payCount": M{"$sum": "$payCount"},
"playCount": M{"$sum": "$playCount"},
"payPlayCount": M{"$sum": "$payPlayCount"},
"tax": M{"$sum": "$tax"},
},
})
list := []SumGroup{}
return list, coll(nil).Aggregate(&list, pipeline)
}
+58
View File
@@ -0,0 +1,58 @@
package statvidmod
import (
"time"
"91porn-server/common/db"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type (
M = bson.M
D = bson.D
ObjectID = primitive.ObjectID
Stat struct {
ID ObjectID `bson:"_id,omitempty"`
SumDate time.Time `bson:"sumDate"`
Vid ObjectID `bson:"vid"`
Income int64 `bson:"income"` //收入
PayCount int64 `bson:"payCount"` //购买次数
PlayCount int64 `bson:"playCount"` //播放次数
PayPlayCount int64 `bson:"payPlayCount"` //付费播放次数
Tax float64 `bson:"tax"` //税
RecordAt time.Time `bson:"recordAt"` //数据记录时间
CreatedAt time.Time `bson:"createdAt"`
UpdatedAt time.Time `bson:"updatedAt"`
}
VideoStatIncDoc struct {
Income *int64 `bson:"income,omitempty"` //收入
PayCount *int64 `bson:"payCount,omitempty"` //购买次数
PlayCount *int64 `bson:"playCount,omitempty"` //播放次数
PayPlayCount *int64 `bson:"payPlayCount,omitempty"` //付费播放次数
Tax *float64 `bson:"tax,omitempty"` //税
}
VideoStatSetDoc struct {
//****
}
VideoStatDoc struct {
VideoStatIncDoc `bson:",inline"`
VideoStatSetDoc `bson:",inline"`
}
VideoStatDocMap = map[ObjectID]*VideoStatDoc
)
func Init() {
mdb = db.Init(table)
initIndex()
}
+117
View File
@@ -0,0 +1,117 @@
package statvidtotalmod
import (
"fmt"
"time"
"91porn-server/common"
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/models"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var mdb *db.MongoDB
const table = models.VideoTotalStat
func coll(t *db.MongoTool) *db.MongoTool {
if t == nil {
return mdb.Coll(table)
}
return t.Coll(table)
}
func initIndex() {
many := []mongo.IndexModel{
{
Keys: bson.D{{Key: "vid", Value: 1}},
Options: options.Index().SetUnique(true),
},
{
Keys: bson.D{{Key: "playCount", Value: -1}},
},
{
Keys: bson.D{{Key: "payCount", Value: -1}},
},
{
Keys: bson.D{{Key: "payPlayCount", Value: -1}},
},
{
Keys: bson.D{{Key: "createdAt", Value: -1}},
},
{
Keys: bson.D{{Key: "updatedAt", Value: -1}},
},
{
Keys: bson.D{{Key: "recordAt", Value: -1}},
},
}
if _, err := coll(nil).CreateIndex(many); err != nil {
panic(fmt.Sprintf("%s model set index err ==>[%+v]", table, err))
}
}
func ChangeStatTrans(trans *db.MongoTool, recordAt time.Time, incDocMap IncDocMap) error {
writes := make([]mongo.WriteModel, len(incDocMap))
i := 0
for vid, doc := range incDocMap {
filter := M{
"vid": vid,
}
incM, err := common.ToBsonM(doc)
if err != nil {
return err
}
update := M{
"$setOnInsert": M{
"vid": vid,
"createdAt": time.Now(),
},
"$set": M{
"updatedAt": time.Now(),
"recordAt": recordAt,
},
}
if len(incM) != 0 {
update["$inc"] = incM
}
writes[i] = mongo.NewUpdateOneModel().
SetFilter(filter).
SetUpdate(update).
SetUpsert(true)
i++
log.Debug(fmt.Sprintf("table:%s [ vid:%+v filter:%+v update:%+v ]\n", table, vid, filter, update))
}
if len(writes) == 0 {
return nil
}
//bulkWrite 不是原子操作 不具备事务性
opt := (&options.BulkWriteOptions{}).SetOrdered(false) //设为无序,触发并行写,提升写效率
_, err := coll(trans).Bulk(writes, opt)
return err
}
func GetPayCountMap(vidList []ObjectID) (map[ObjectID]int64, error) {
if len(vidList) == 0 {
return make(map[ObjectID]int64), nil
}
filter := M{
"vid": M{"$in": vidList},
}
list := []struct {
Vid ObjectID `bson:"vid"`
PayCount int64 `bson:"payCount"` //购买次数
}{}
if err := coll(nil).Find(&list, filter); err != nil {
return make(map[ObjectID]int64), nil
}
m := make(map[ObjectID]int64, len(list))
for _, v := range list {
m[v.Vid] = v.PayCount
}
return m, nil
}
+48
View File
@@ -0,0 +1,48 @@
package statvidtotalmod
import (
"time"
"91porn-server/common/db"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type (
M = bson.M
D = bson.D
ObjectID = primitive.ObjectID
Stat struct {
ID ObjectID `bson:"_id,omitempty"`
Vid ObjectID `bson:"vid"`
Income int64 `bson:"income"` //收入
PayCount int64 `bson:"payCount"` //购买次数
PlayCount int64 `bson:"playCount"` //播放次数
PayPlayCount int64 `bson:"payPlayCount"` //付费播放次数
Tax float64 `bson:"tax"` //[未完成]税
RecordAt time.Time `bson:"recordAt"`
CreatedAt time.Time `bson:"createdAt"`
UpdatedAt time.Time `bson:"updatedAt"`
}
IncDoc struct {
Income int64 `bson:"income"` //收入
PayCount int64 `bson:"payCount"` //购买次数
PlayCount int64 `bson:"playCount"` //播放次数
PayPlayCount int64 `bson:"payPlayCount"` //付费播放次数
Tax float64 `bson:"tax"` //[未完成]税
}
IncDocMap = map[ObjectID]*IncDoc
)
func Init() {
mdb = db.Init(table)
initIndex()
}
+63
View File
@@ -0,0 +1,63 @@
package useradverstatmod
import (
"91porn-server/common/log"
"91porn-server/models/commod"
"fmt"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
type DailyRetentionSummary struct {
ID DailyRetentionSummaryID `bson:"_id"`
Retained int64 `bson:"retained"`
AdClick int64 `bson:"adClick"`
TotalClick int64 `bson:"totalClick"`
PayCount int64 `bson:"payCount"`
PayTotal int64 `bson:"payTotal"`
}
type DailyRetentionSummaryID struct {
RegDay time.Time `bson:"regDay"`
AdGroup commod.AdGroup `bson:"adGroup"`
}
func AggregateDailyRetention(date, regDayStart, regDayEnd time.Time) (out []*DailyRetentionSummary, err error) {
pipeline := []bson.M{
{
"$match": bson.M{
"date": date,
"regDay": bson.M{
"$gte": regDayStart,
"$lte": regDayEnd,
},
},
},
{
"$group": bson.M{
"_id": bson.M{
"regDay": "$regDay",
"adGroup": "$adGroup",
},
"retained": bson.M{"$sum": 1},
"adClick": bson.M{"$sum": "$adClick"},
"totalClick": bson.M{"$sum": "$totalClick"},
"payCount": bson.M{"$sum": "$payCount"},
"payTotal": bson.M{"$sum": "$payTotal"},
},
},
}
opts := options.Aggregate().SetAllowDiskUse(true)
if err = coll(nil).Aggregate(&out, pipeline, opts); err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "AggregateDailyRetention", table, "Aggregate", err),
log.Any("date", date),
log.Any("regDayStart", regDayStart),
log.Any("regDayEnd", regDayEnd),
)
return nil, err
}
return
}
+29
View File
@@ -0,0 +1,29 @@
package useradverstatmod
import (
"91porn-server/common/db"
"91porn-server/models/commod"
"time"
"go.mongodb.org/mongo-driver/bson/primitive"
)
var mdb *db.MongoDB
type UserAdStat struct {
Id primitive.ObjectID `json:"id" bson:"_id"` // 自增ID
UserId int64 `json:"userId" bson:"userId" index:"user_idx,user_date_idx:unique"` // 用户Id
Date time.Time `json:"date" bson:"date" index:"-user_idx,user_date_idx:unique"` // 日期(当日0点)
DistrictCode string `json:"districtCode" bson:"districtCode"` // 渠道码(只统计有渠道码的)
SysType string `json:"sysType" bson:"sysType"` // 系统类型(目前只统计安卓)
AdGroup commod.AdGroup `json:"adGroup" bson:"adGroup" index:"ad_group"` // 广告组
AdClick int64 `json:"adClick" bson:"adClick"` // 广告点击
TotalClick int64 `json:"totalClick" bson:"totalClick"` // 广告+APP总点击
AppClick int64 `json:"appClick" bson:"appClick"` // APP点击
PayCount int64 `json:"payCount" bson:"payCount"` // 充值次数
PayTotal int64 `json:"payTotal" bson:"payTotal"` // 充值总金额
RegDay time.Time `json:"regDay" bson:"regDay" index:"-reg_day_idx"` // 注册日期(用户注册当天0点)
Version string `json:"version" bson:"version"` // 版本号
CreatedAt time.Time `json:"createdAt" bson:"createdAt" index:"-created_at_idx"` // 创建时间
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"` // 更新时间
}
@@ -0,0 +1,94 @@
package useradverstatmod
import (
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/models"
"fmt"
"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 table = models.UserAdverStat
func coll(t *db.MongoTool) *db.MongoTool {
if t == nil {
return mdb.Coll(table)
}
return t.Coll(table)
}
func initIndex() {
many := []mongo.IndexModel{
{
Keys: bson.D{
{Key: "adGroup", Value: 1},
},
},
{
Keys: bson.D{
{Key: "date", Value: -1},
},
},
{
Keys: bson.D{
{Key: "regDay", Value: -1},
},
},
{
Keys: bson.D{
{Key: "date", Value: 1},
{Key: "regDay", Value: 1},
{Key: "adGroup", Value: 1},
},
},
{
Keys: bson.D{
{Key: "createdAt", Value: -1},
},
},
{
Keys: bson.D{
{Key: "userId", Value: 1},
{Key: "date", Value: -1},
},
Options: options.Index().SetUnique(true),
},
}
if _, err := coll(nil).CreateIndex(many); err != nil {
panic(fmt.Sprintf("%s model set index err ==>[%+v]", table, err))
}
}
// Init 初始化索引
func Init() {
mdb = db.Init(table)
initIndex()
}
// QueryAllList 分页查询文档
func QueryAllList(filter primitive.M, opts ...*options.FindOptions) (out []*UserAdStat, err error) {
if err = coll(nil).Find(&out, filter, opts...); err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "QueryAllList", table, "Find", err),
log.Any("filter", filter),
log.Any("opts", opts),
)
return nil, err
}
return
}
// UpsertOne
func UpsertOne(filter bson.M, updateData bson.M) (err error) {
if _, err = coll(nil).UpsertOne(filter, updateData); err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "IncrCommentCountByID", table, "UpdateOne", err),
log.Any("filter", filter),
log.Any("updateData", updateData),
)
return
}
return
}