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
+190
View File
@@ -0,0 +1,190 @@
package txnmod
import (
"fmt"
"strings"
"time"
"91porn-server/common/log"
"91porn-server/common/pageopt"
"91porn-server/common/ysinterface/disc"
"91porn-server/models/commod"
)
type DiscSeqe = commod.DiscSeqe
type DistrictStatKey struct {
DiscSeqe `bson:",inline"` //商区码
SysType string `bson:"sysType"` //系统类型 iOS Android
}
func (d DistrictStatKey) GetDiscCode() string {
return strings.ToUpper(d.DistrictCode)
}
func (d DistrictStatKey) GetPromSeqe() string {
return d.PromSeqe
}
func (d DistrictStatKey) GetSysType() string {
return d.SysType
}
func (d DistrictStatKey) String() string {
if d.DiscSeqe.String() == "" {
return ""
}
return strings.ToUpper(strings.Join([]string{d.DiscSeqe.String(), d.SysType}, "-"))
}
type DistrictStater = disc.DistrictStater
// DiscSeqeTransCount
func DiscSeqeTransCount(start, end time.Time, mats ...Matcher) (map[DistrictStater]int64, error) {
mats = append(mats, (&CreatedAtGTEAndLTMatch{GTE: &start, LT: &end}).New())
filter := pageopt.MergeM(mats)
list := []DistrictStatKey{}
if err := coll(nil).Find(&list, filter); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "DiscSeqeTransCount", table, "Find", err))
return nil, err
}
m := make(map[DistrictStater]int64)
for _, v := range list {
if v.String() != "" {
m[v] += 1
}
}
return m, nil
}
// DiscSeqeTransAmount
func DiscSeqeTransAmount(start, end time.Time, mats ...Matcher) (map[DistrictStater]int64, error) {
mats = append(mats, (&CreatedAtGTEAndLTMatch{GTE: &start, LT: &end}).New())
filter := pageopt.MergeM(mats)
list := []struct {
DistrictStatKey `bson:",inline"` //商区码
Amount int64 `bson:"amount"`
}{}
if err := coll(nil).Find(&list, filter); err != nil {
return nil, err
}
m := make(map[DistrictStater]int64, len(list))
for _, v := range list {
if v.String() != "" {
m[v.DistrictStatKey] += v.Amount
}
}
return m, nil
}
// DiscTransUIDSMap
func DiscTransUIDSMap(start, end time.Time, mats ...Matcher) (map[DistrictStater][]uint64, error) {
mats = append(mats, (&CreatedAtGTEAndLTMatch{GTE: &start, LT: &end}).New())
filter := pageopt.MergeM(mats)
list := []struct {
DistrictCode string `json:"districtCode" bson:"districtCode"` //商区码
UID uint64 `bson:"uid"`
}{}
if err := coll(nil).Find(&list, filter); err != nil {
return nil, err
}
m := make(map[DistrictStater][]uint64, len(list))
for _, v := range list {
key := DistrictStatKey{
DiscSeqe: DiscSeqe{DistrictCode: v.DistrictCode},
}
if key.String() != "" {
m[key] = append(m[key], v.UID)
}
}
return m, nil
}
// DiscSeqeTransUIDSMap
func DiscSeqeTransUIDSMap(start, end time.Time, mats ...Matcher) (map[DistrictStater][]uint64, error) {
mats = append(mats, (&CreatedAtGTEAndLTMatch{GTE: &start, LT: &end}).New())
filter := pageopt.MergeM(mats)
list := []struct {
DistrictStatKey `bson:",inline"` //商区码
UID uint64 `bson:"uid"`
}{}
if err := coll(nil).Find(&list, filter); err != nil {
return nil, err
}
m := make(map[DistrictStater][]uint64, len(list))
for _, v := range list {
if v.String() != "" {
m[v.DistrictStatKey] = append(m[v.DistrictStatKey], v.UID)
}
}
return m, nil
}
// DiscSeqeTransTaxAmount
func DiscSeqeTransTaxAmount(start, end time.Time, mats ...Matcher) (map[DistrictStater]float64, error) {
mats = append(mats, (&CreatedAtGTEAndLTMatch{GTE: &start, LT: &end}).New())
filter := pageopt.MergeM(mats)
list := []struct {
DistrictStatKey `bson:",inline"` //商区码
TaxAmount float64 `bson:"taxAmount"` //税额
}{}
if err := coll(nil).Find(&list, filter); err != nil {
return nil, err
}
m := make(map[DistrictStater]float64, len(list))
for _, v := range list {
if v.String() != "" {
m[v.DistrictStatKey] += v.TaxAmount
}
}
return m, nil
}
// DiscSeqeBuyVipCount 时间内购买VIP次数
func DiscSeqeBuyVipCount(start, end time.Time, mats ...Matcher) (map[DistrictStater]int64, error) {
payVIP := PayVIP.Key()
mats = append(mats, (&TranTypeMatch{&payVIP}).New())
return DiscSeqeTransCount(start, end, mats...)
}
// DiscSeqeBuyVipCount 时间内购买VIP金额
func DiscSeqeBuyVipAmount(start, end time.Time, mats ...Matcher) (map[DistrictStater]int64, error) {
payVIP := PayVIP.Key()
mats = append(mats, (&TranTypeMatch{&payVIP}).New())
return DiscSeqeTransAmount(start, end, mats...)
}
// DiscBuyVipUIDSMap 时间内购买用户列表
func DiscBuyVipUIDSMap(start, end time.Time, mats ...Matcher) (map[DistrictStater][]uint64, error) {
payVIP := PayVIP.Key()
mats = append(mats, (&TranTypeMatch{&payVIP}).New())
return DiscTransUIDSMap(start, end, mats...)
}
// DiscSeqeBuyVipUIDSMap 时间内购买用户列表
func DiscSeqeBuyVipUIDSMap(start, end time.Time, mats ...Matcher) (map[DistrictStater][]uint64, error) {
payVIP := PayVIP.Key()
mats = append(mats, (&TranTypeMatch{&payVIP}).New())
return DiscSeqeTransUIDSMap(start, end, mats...)
}
// DiscSeqeBuyVidCount 时间内购买VID次数
func DiscSeqeBuyVidCount(start, end time.Time, mats ...Matcher) (map[DistrictStater]int64, error) {
payVID := PayVID.Key()
mats = append(mats, (&TranTypeMatch{&payVID}).New())
return DiscSeqeTransCount(start, end, mats...)
}
// DiscSeqeBuyVidAmount 时间内购买VID金额
func DiscSeqeBuyVidAmount(start, end time.Time, mats ...Matcher) (map[DistrictStater]int64, error) {
payVID := PayVID.Key()
mats = append(mats, (&TranTypeMatch{&payVID}).New())
return DiscSeqeTransAmount(start, end, mats...)
}
// DiscSeqeWorksIncomeTaxAmount 时间内购买VID税额
func DiscSeqeWorksIncomeTaxAmount(start, end time.Time, mats ...Matcher) (map[DistrictStater]float64, error) {
worksIncome := WorksIncome.Key()
mats = append(mats, (&TranTypeMatch{&worksIncome}).New())
return DiscSeqeTransTaxAmount(start, end, mats...)
}
+214
View File
@@ -0,0 +1,214 @@
package txnmod
import (
"bytes"
"errors"
"sort"
"time"
"91porn-server/models"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
)
const ledgerQueryTimeout = 5 * time.Second
const maxLedgerWindow int64 = 100000
var ledgerSort = bson.D{{Key: "createdAt", Value: -1}, {Key: "_id", Value: -1}}
// Fund records have no global time index. Only a concrete user's ledger can
// include them without introducing a collection scan into the admin listing.
func hasLedgerUID(filter bson.M) bool {
switch uid := filter["uid"].(type) {
case uint64:
return uid > 0
case int64:
return uid > 0
case int:
return uid > 0
case int32:
return uid > 0
}
return false
}
// Normalize at read time: no historical transaction or wallet is rewritten.
// The indexable user/time match precedes projection and derived-field filters.
func fundLedgerPipeline(filter bson.M, ordered bool) []bson.M {
if !hasLedgerUID(filter) {
return nil
}
if kind, ok := filter["tranType"].(string); ok && kind != AiGirlfriendTransferIn.Key() && kind != AiGirlfriendTransferOut.Key() {
return nil
}
match := bson.M{"uid": filter["uid"], "category": 0, "fundType": bson.M{"$in": []int{1, 2}}}
if period, ok := filter["createdAt"]; ok {
match["createdAt"] = period
}
in := bson.M{"$eq": bson.A{"$fundType", 1}}
amount := bson.M{"$cond": bson.A{in, bson.M{"$multiply": bson.A{"$amount", -1}}, "$amount"}}
kind := bson.M{"$cond": bson.A{in, AiGirlfriendTransferIn.Key(), AiGirlfriendTransferOut.Key()}}
pipeline := []bson.M{{"$match": match}}
if ordered {
pipeline = append(pipeline, bson.M{"$sort": ledgerSort})
}
pipeline = append(pipeline,
bson.M{"$project": bson.M{
"_id": 1, "uid": 1, "createdAt": 1,
"purchaseOrder": "$_id", "amount": amount,
"actualAmount": bson.M{"$toDouble": amount},
"integral": bson.M{"$literal": 0},
"tranType": kind,
"tranTypeInt": bson.M{"$cond": bson.A{in, AiGirlfriendTransferIn, AiGirlfriendTransferOut}},
"desc": bson.M{"$ifNull": bson.A{"$desc", kind}},
"realAmount": bson.M{"$toDecimal": "$balance"},
}},
bson.M{"$match": filter},
)
return pipeline
}
func mergeLedgerRows(ordinary, transfers []*TransactionLog, skip, limit int64) []*TransactionLog {
rows := append(ordinary, transfers...)
sort.SliceStable(rows, func(i, j int) bool {
if !rows[i].CreatedAt.Equal(rows[j].CreatedAt) {
return rows[i].CreatedAt.After(rows[j].CreatedAt)
}
return bytes.Compare(rows[i].ID[:], rows[j].ID[:]) > 0
})
if skip >= int64(len(rows)) {
return []*TransactionLog{}
}
end := int64(len(rows))
if limit > 0 && skip+limit < end {
end = skip + limit
}
return rows[skip:end]
}
// Read only the prefix needed from each source, then paginate the merged order.
// An explicit window and server timeout bound old monthly and export requests.
func findLedgerRows(filter bson.M, skip, limit int64) ([]*TransactionLog, error) {
if skip < 0 || limit < 0 || skip > maxLedgerWindow || limit > maxLedgerWindow || (limit > 0 && skip > maxLedgerWindow-limit) {
return nil, errors.New("ledger query window is too large; narrow the time range")
}
window := skip + limit
if limit == 0 {
window = maxLedgerWindow + 1
}
rows := []*TransactionLog{}
pipeline := fundLedgerPipeline(filter, true)
opt := options.Find().SetSort(ledgerSort).SetMaxTime(ledgerQueryTimeout)
if !hasLedgerUID(filter) {
opt.SetSort(bson.D{{Key: "createdAt", Value: -1}})
}
if pipeline == nil && limit > 0 {
opt.SetSkip(skip).SetLimit(limit)
} else {
opt.SetLimit(window)
}
if err := coll(nil).Find(&rows, filter, opt); err != nil {
return nil, err
}
if pipeline == nil && limit > 0 {
return rows, nil
}
transfers := []*TransactionLog{}
if pipeline != nil {
pipeline = append(pipeline, bson.M{"$limit": window})
if err := mdb.Coll(models.FundTransferLog).Aggregate(&transfers, pipeline, options.Aggregate().SetMaxTime(ledgerQueryTimeout)); err != nil {
return nil, err
}
}
if limit == 0 && int64(len(rows)+len(transfers)) > maxLedgerWindow {
return nil, errors.New("ledger query window is too large; narrow the time range")
}
return mergeLedgerRows(rows, transfers, skip, limit), nil
}
func countLedgerRows(filter bson.M) (int64, error) {
count, err := coll(nil).Count(filter, options.Count().SetMaxTime(ledgerQueryTimeout))
if err != nil {
return 0, err
}
pipeline := fundLedgerPipeline(filter, false)
if pipeline == nil {
return count, nil
}
var result []struct {
Count int64 `bson:"count"`
}
pipeline = append(pipeline, bson.M{"$count": "count"})
if err := mdb.Coll(models.FundTransferLog).Aggregate(&result, pipeline, options.Aggregate().SetMaxTime(ledgerQueryTimeout)); err != nil {
return 0, err
}
if len(result) > 0 {
count += result[0].Count
}
return count, nil
}
func getLedgerCoinLogs(page, size uint64, filter bson.M) ([]*TransactionLog, uint64, int64, error) {
if size == 0 || size > uint64(maxLedgerWindow) {
return nil, 0, 0, errors.New("invalid ledger page size")
}
total, err := countLedgerRows(filter)
if err != nil {
return nil, 0, 0, err
}
pages := (uint64(total) + size - 1) / size
if pages == 0 {
return []*TransactionLog{}, 0, 0, nil
}
if page == 0 {
page = 1
}
if page > pages {
page = pages
}
if page-1 > uint64(maxLedgerWindow)/size {
return nil, pages, total, errors.New("ledger query window is too large; narrow the time range")
}
rows, err := findLedgerRows(filter, int64((page-1)*size), int64(size))
return rows, pages, total, err
}
func FindLedgerByTime(uid uint64, start, end time.Time) ([]*TransactionLog, error) {
return findLedgerRows(bson.M{"uid": uid, "createdAt": bson.M{"$gte": start, "$lt": end}}, 0, 0)
}
func LastLedgerTime(uid uint64, end time.Time) (TransactionLog, error) {
rows, err := findLedgerRows(bson.M{"uid": uid, "createdAt": bson.M{"$lt": end}}, 0, 1)
if err != nil || len(rows) == 0 {
return TransactionLog{}, err
}
return *rows[0], nil
}
// These are wallet inflow/outflow totals, not recharge, creator or agent revenue.
func sumLedgerAmount(uid uint64, start, end time.Time, income bool) (float64, error) {
op := "$lt"
if income {
op = "$gt"
}
filter := bson.M{"uid": uid, "createdAt": bson.M{"$gte": start, "$lt": end}, "actualAmount": bson.M{op: 0}}
group := bson.M{"$group": bson.M{"_id": nil, "total": bson.M{"$sum": "$actualAmount"}}}
pipelines := [][]bson.M{{{"$match": filter}, group}, append(fundLedgerPipeline(filter, false), group)}
var total float64
for i, name := range []string{table, models.FundTransferLog} {
var result []struct {
Total float64 `bson:"total"`
}
if i == 1 && !hasLedgerUID(filter) {
continue
}
if err := mdb.Coll(name).Aggregate(&result, pipelines[i], options.Aggregate().SetMaxTime(ledgerQueryTimeout)); err != nil {
return 0, err
}
if len(result) > 0 {
total += result[0].Total
}
}
return total, nil
}
+128
View File
@@ -0,0 +1,128 @@
package txnmod
import (
"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
// TranTypeMatch
type TranTypeMatch struct {
TransType *string
}
func (s *TranTypeMatch) New() Matcher {
return pageopt.NewAssignMatch("tranType", s.TransType)
}
// DistrictCodeMatch
type DistrictCodeMatch struct {
DistrictCode *string
}
func (s *DistrictCodeMatch) New() Matcher {
return pageopt.NewAssignMatch("districtCode", s.DistrictCode)
}
// DistrictCodeINMatch
type DistrictCodeINMatch struct {
DistrictCodeList []string
}
func (d *DistrictCodeINMatch) New() Matcher {
return pageopt.NewInMatch("districtCode", d.DistrictCodeList)
}
// PromSeqeMatch
type PromSeqeMatch struct {
Seqe *string
}
func (d *PromSeqeMatch) New() Matcher {
return pageopt.NewAssignMatch("promSeqe", d.Seqe)
}
// SysTypeMatch
type SysTypeMatch struct {
SysType *string
}
func (d *SysTypeMatch) New() Matcher {
return pageopt.NewAssignMatch("sysType", d.SysType)
}
// IsDirectMatch
type IsDirectMatch struct {
IsDirect *bool
}
func (b *IsDirectMatch) New() Matcher {
return pageopt.NewAssignMatch("isDirect", b.IsDirect)
}
// CreatedAtGTEAndLTMatch
type CreatedAtGTEAndLTMatch = pageopt.CreatedAtGTEAndLTMatch
// UIDMatch
type UIDMatch = pageopt.UIDMatch
// UIDInMatch
type UIDInMatch = pageopt.UIDInMatch
// IDMatch
type IDMatch = pageopt.IDMatch
type Sort = bson.D
var Sort_CreatedAt_n1 = Sort{{Key: "createdAt", Value: -1}}
func List(sort Sort, skip, limit *int64, matchers ...Matcher) ([]TransactionLog, 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 := []TransactionLog{}
if err := coll(nil).Find(&list, filter, opt); err != nil {
log.Error("txnmod 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("txnmod Count error", log.E(err))
return 0, err
}
return count, nil
}
func AmountSum(matchers ...Matcher) (int64, error) {
filter := pageopt.MergeM(matchers)
pipeline := []M{}
pipeline = append(pipeline, M{"$match": filter})
pipeline = append(pipeline, M{"$group": M{"_id": nil,
"amount": M{"$sum": "$amount"},
}})
var amount struct {
Amount int64 `bson:"amount"` //金币
}
if err := coll(nil).AggregateDecode(&amount, pipeline); err != nil {
log.Error("txnmod Sum error", log.E(err))
return 0, err
}
return amount.Amount, nil
}
+654
View File
@@ -0,0 +1,654 @@
/*
* @Description: In User Settings Edit
* @Author: your name
* @Date: 2019-08-29 19:55:45
* @LastEditTime: 2019-08-30 19:39:56
* @LastEditors: Please set LastEditors
*/
package txnmod
import (
"fmt"
"math"
"time"
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/common/pageopt"
"91porn-server/common/timeutil"
"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"
)
const table = models.Transaction
// InitIndex 设置index
func initIndex() {
coll := coll(nil)
many := []mongo.IndexModel{
{
Keys: bson.D{{Key: "uid", Value: 1}},
},
{
Keys: bson.D{{Key: "purchaseOrder", Value: 1}},
},
{
Keys: bson.D{{Key: "uid", Value: 1}, {Key: "createdAt", Value: -1}},
},
{
Keys: bson.D{{Key: "amount", Value: 1}},
},
{
Keys: bson.D{{Key: "actualAmount", Value: 1}},
},
{
Keys: bson.D{{Key: "createdAt", Value: 1}},
},
{
Keys: bson.D{{Key: "uniqueOrder", Value: 1}},
Options: options.Index().SetSparse(true).SetUnique(true),
},
{
Keys: bson.D{{Key: "tranType", Value: 1}, {Key: "createdAt", Value: -1}},
// Options: options.Index().SetUnique(true).SetSparse(true),
},
{
Keys: bson.D{{Key: "tranType", Value: 1}, {Key: "districtCode", Value: 1}, {Key: "promSeqe", Value: 1}, {Key: "createdAt", Value: -1}},
Options: options.Index().SetSparse(true),
},
{
Keys: bson.D{{Key: "tranType", Value: 1}, {Key: "districtCode", Value: 1}, {Key: "promSeqe", Value: 1}, {Key: "sysType", Value: 1}, {Key: "createdAt", Value: -1}},
Options: options.Index().SetSparse(true),
},
{
Keys: bson.D{{Key: "tranTypeInt", Value: 1}, {Key: "createdAt", Value: -1}},
},
{
Keys: bson.D{{Key: "tranTypeInt", Value: 1}, {Key: "districtCode", Value: 1}, {Key: "promSeqe", Value: 1}, {Key: "createdAt", Value: -1}},
Options: options.Index().SetSparse(true),
},
{
Keys: bson.D{{Key: "tranTypeInt", Value: 1}, {Key: "districtCode", Value: 1}, {Key: "promSeqe", Value: 1}, {Key: "sysType", Value: 1}, {Key: "createdAt", Value: -1}},
Options: options.Index().SetSparse(true),
},
{
Keys: bson.D{{Key: "isDirect", Value: 1}, {Key: "createdAt", Value: -1}},
Options: options.Index().SetSparse(true),
},
{
Keys: bson.D{{Key: "uid", Value: 1}, {Key: "tranTypeInt", Value: 1}, {Key: "money", Value: 1}, {Key: "fruitCoin", Value: 1}, {Key: "createdAt", Value: -1}},
Options: options.Index().SetSparse(true),
},
{
Keys: bson.D{{Key: "deductType", Value: 1}, {Key: "createdAt", Value: -1}},
Options: options.Index().SetSparse(true),
},
}
if _, err := coll.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)
}
// Insert 新增
func InsertTransactionLog(mt *db.MongoTool, t *TransactionLog) error {
//Insert 插入一条交易流水
t.CreatedAt = time.Now()
if _, err := coll(mt).InsertOne(t); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "InsertTransactionLog", table, "InsertOne", err))
return err
}
return nil
}
func InsertManyTransactionLog(mt *db.MongoTool, trans []TransactionLog) error {
ops := options.InsertMany().SetOrdered(false)
if _, err := coll(mt).InsertMany(trans, ops); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "InsertTransactionLog", table, "InsertOne", err))
return err
}
return nil
}
// FindMany 查询所有
func FindTransactionLogs(filter bson.M, opts *options.FindOptions) (total int64, data []*TransactionLog, err error) {
data = make([]*TransactionLog, 0)
if err = coll(nil).Find(&data, filter, opts); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "FindTransactionLogs", table, "Find", err),
log.Any("filter", filter),
)
return
}
total, err = coll(nil).Count(filter)
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "FindTransactionLogs", table, "Count", err),
log.Any("filter", filter),
)
}
return
}
// getTotalCnt 获取标签视频总数
func getTotalCnt(cond bson.M) (int64, error) {
total, err := coll(nil).Count(cond)
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "getTotalCnt", table, "Count", err),
log.Any("cond", cond),
)
return 0, err
}
return total, nil
}
// GetSkipSize 计算跳转
func getSkipSize(page, size uint64, cond bson.M) (uint64, uint64, int64, error) {
total, err := getTotalCnt(cond)
if err != nil {
return 0, 0, 0, err
}
totalpages := uint64(math.Ceil(float64(total) / float64(size)))
if page > totalpages {
page = totalpages
}
if page < 1 {
page = 1
}
return (page - 1) * size, totalpages, total, nil
}
// GetCoinLogs 条件获取金币日志(时间倒序)
func GetCoinLogs(page, size uint64, cond map[string]interface{}) ([]*TransactionLog, uint64, int64, error) {
if !hasLedgerUID(bson.M(cond)) {
// Preserve the existing global listing and export path until the fund
// collection has an approved index for cross-user time queries.
skip, totalPages, total, err := getSkipSize(page, size, bson.M(cond))
if err != nil {
return nil, 0, 0, err
}
opts := options.Find().SetSort(bson.D{{Key: "createdAt", Value: -1}}).
SetSkip(int64(skip)).SetLimit(int64(size))
var rows []*TransactionLog
err = coll(nil).Find(&rows, bson.M(cond), opts)
return rows, totalPages, total, err
}
return getLedgerCoinLogs(page, size, bson.M(cond))
}
func GetTranTypes() (data []interface{}, err error) {
data, err = coll(nil).Distinct("tranType", bson.M{})
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "GetTranTypes", table, "Distinct", err))
return nil, err
}
for _, kind := range []string{AiGirlfriendTransferIn.Key(), AiGirlfriendTransferOut.Key()} {
found := false
for _, existing := range data {
if existing == kind {
found = true
break
}
}
if !found {
data = append(data, kind)
}
}
return
}
// FindManyByTime 查询所有
func FindManyByTime(uid uint64, start time.Time, end time.Time) (data []*TransactionLog, err error) {
data = make([]*TransactionLog, 0)
opts := options.FindOptions{
Sort: bson.D{{Key: "createdAt", Value: -1}},
}
if err = coll(nil).Find(&data, bson.M{"uid": uid, "createdAt": bson.M{"$gte": start, "$lt": end}}, &opts); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "FindManyByTime", table, "Find", err),
log.Any("uid", uid),
log.Any("start", start),
log.Any("end", end),
)
return
}
return
}
// HasNext 查询所有
func HasNext(uid uint64, start time.Time) (hasNext bool, err error) {
var data TransactionLog
if err = coll(nil).FindOne(&data, bson.M{"uid": uid, "createdAt": bson.M{"$lt": start}}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "HasNext", table, "FindOne", err),
log.Any("uid", uid),
log.Any("start", start),
)
return
}
return !data.ID.IsZero(), err
}
// LastTime 查询所有
func LastTime(uid uint64, end time.Time) (data TransactionLog, err error) {
opt := options.FindOneOptions{
Sort: bson.D{{Key: "createdAt", Value: -1}},
}
if err = coll(nil).FindOne(&data, bson.M{"uid": uid, "createdAt": bson.M{"$lt": end}}, &opt); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "LastTime", table, "FindOne", err),
log.Any("uid", uid),
log.Any("end", end),
)
return
}
return data, nil
}
func GetBuyVipCountMapByHour(discCode string, start, end time.Time, mats ...Matcher) ([]int, map[int]int64, int64, error) {
payVIP := PayVIP.Key()
mats = append(mats,
(&TranTypeMatch{&payVIP}).New(),
(&DistrictCodeMatch{&discCode}).New(),
(&CreatedAtGTEAndLTMatch{GTE: &start, LT: &end}).New())
filter := pageopt.MergeM(mats)
opt := (&options.FindOptions{}).SetProjection(M{
"createdAt": 1,
})
var list []struct {
CreatedAt time.Time `bson:"createdAt"` //创建时间
}
if err := coll(nil).Find(&list, filter, opt); err != nil {
log.Error("txnmod GetBuyVipCountMap error", log.E(err))
return nil, nil, 0, err
}
m := make(map[int]int64)
startH := start.Hour()
lastH := end.Hour() + (end.Day()-start.Day())*24
hs := make([]int, lastH-startH+1)
for i, j := startH, 0; i <= lastH; i++ {
hs[j] = i
m[i] = 0
j++
}
var total int64
for _, v := range list {
hour := v.CreatedAt.Hour() + (v.CreatedAt.Day()-start.Day())*24
m[hour] += 1
total++
}
return hs, m, total, nil
}
// FindIncome 查询所有
func FindIncome(uid uint64, pageNumebr, pageSize uint64) (data []*TransactionLog, hasNext bool, err error) {
data = make([]*TransactionLog, 0)
f := bson.M{"uid": uid, "actualAmount": bson.M{"$gt": 0}}
skip := int64(pageSize * (pageNumebr - 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.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "FindTransactionLogs", table, "Find", err),
log.Any("filter", f),
)
return
}
if len(data) > int(pageSize) {
hasNext = true
data = data[:pageSize]
}
return
}
// FindPoxyIncomeOfMonth 查询每月收益
func FindPoxyIncomeOfMonth(uid uint64) (totalMoney int64, totalPerfomance int64, err error) {
firstDay, lastDay := timeutil.MonthStartEndTime(time.Now())
pipelines := []bson.M{
{"$match": bson.M{"uid": uid, "tranTypeInt": ProxyIncome, "createdAt": bson.M{"$gte": firstDay, "$lt": lastDay}}},
{"$group": bson.M{"_id": nil, "totalMoney": bson.M{"$sum": "$actualAmount"}, "totalPerformance": bson.M{"$sum": "$performance"}}},
{"$project": bson.M{"totalMoney": 1, "totalPerformance": 1}},
}
type res struct {
TotalMoney int64 `bson:"totalMoney"`
TotalPerformance int64 `bson:"totalPerformance"`
}
data := make([]res, 0)
if err = coll(nil).Aggregate(&data, pipelines); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "FindIncomeOfMonth", table, "Aggregate", err), log.Any("uid", uid))
return
}
if len(data) > 0 {
totalMoney = data[0].TotalMoney
totalPerfomance = data[0].TotalPerformance
}
return
}
// FindTransactionLogsByTransType 查询所有
func FindTransactionLogsByTransType(uid uint64, pageNumber int64, pageSize int64, tranType TransType) (data []*TransactionLog, hasNext bool, err error) {
data = make([]*TransactionLog, 0)
skip := (pageNumber - 1) * pageSize
limit := pageSize + 1
opts := options.FindOptions{
Skip: &skip,
Limit: &limit,
Sort: bson.D{{Key: "createdAt", Value: -1}},
}
if err = coll(nil).Find(&data, bson.M{"uid": uid, "tranTypeInt": tranType}, &opts); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "FindTransactionLogsByTransType", table, "Find", err), log.Any("uid", uid))
return
}
if len(data) > int(pageSize) {
hasNext = true
data = data[:pageSize]
}
return
}
// GetProxyIncomeForToday 查询所有
func GetProxyIncomeForToday(uid uint64, start time.Time, end time.Time) (data []AgentIncomeRes, err error) {
pipelines := []bson.M{
{"$match": bson.M{"uid": uid, "tranTypeInt": ProxyIncome, "createdAt": bson.M{"$gte": start, "$lt": end}}},
{"$group": bson.M{"_id": "$agentLevel", "totalAmount": bson.M{"$sum": "$actualAmount"}, "totalPerformance": bson.M{"$sum": "$performance"}}},
{"$project": bson.M{"totalAmount": 1, "totalPerformance": 1}},
}
data = make([]AgentIncomeRes, 0)
if err = coll(nil).Aggregate(&data, pipelines); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "GetProxyIncomeOfDay", table, "Find", err), log.Any("uid", uid))
return
}
return
}
// GetIncomeByType 查询所有
func GetIncomeByType(uid uint64, tp TransType, start time.Time, end time.Time) (totalAmount float64, totalPer int64, err error) {
match := bson.M{"uid": uid, "tranTypeInt": tp, "createdAt": bson.M{"$gte": start, "$lt": end}}
log.Info(fmt.Sprintf("%v", match))
pipelines := []bson.M{
{"$match": match},
{"$group": bson.M{"_id": nil, "totalAmount": bson.M{"$sum": "$actualAmount"}, "totalPerformance": bson.M{"$sum": "$performance"}}},
{"$project": bson.M{"totalAmount": 1, "totalPerformance": 1}},
}
var res struct {
TotalAmount float64 `bson:"totalAmount"`
TotalPerformance int64 `bson:"totalPerformance"`
}
if err = coll(nil).AggregateDecode(&res, pipelines); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "GetProxyIncomeOfDay", table, "Find", err), log.Any("uid", uid))
return
}
totalAmount = res.TotalAmount
totalPer = res.TotalPerformance
return
}
// IsProxyExist 代理分成是否存在
func IsProxyExist(uid uint64, objId primitive.ObjectID) bool {
filter := bson.M{"uid": uid, "purchaseOrder": objId, "tranType": ProxyIncome.Key()}
count, err := coll(nil).Count(filter)
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "IsProxyExist", table, "Count", count), log.Any("uid", uid), log.Any("objId", objId))
//查询异常的时候, 默认不分成, 用户找来可以人工修复, 项目不能亏
return true
}
if count > 0 {
return true
}
return false
}
// Income 收益
func Income(uid uint64, start time.Time, end time.Time) (totalAmount float64, err error) {
return sumLedgerAmount(uid, start, end, true)
}
// Expenditure 支出
func Expenditure(uid uint64, start time.Time, end time.Time) (totalAmount float64, err error) {
return sumLedgerAmount(uid, start, end, false)
}
// Ibills 账单
func Ibills(uid uint64, pageNumebr, pageSize uint64, start time.Time, end time.Time) (data []TransactionLog, hasNext bool, err error) {
data = make([]TransactionLog, 0)
f := bson.M{
"uid": uid,
"createdAt": bson.M{"$gte": start, "$lt": end},
"tranTypeInt": bson.M{"$in": []TransType{
AiGirlfriendTransferIn, AiGirlfriendTransferOut, Rchg, PayVID, WithdrawTransfer, PayVIP, WithdrawRefund, AdminCreaditAmount, AdminDebitAmount, OfficialRech, MeetingCard,
GameCoin, ChaseScore, PayReward, Other, LouFeng, AudioBook, LouFengRefund, LouFengDiscount, GameRewards, VipCardGive,
LouFengMianFei, LuckyDraw, PayAvVID, TranType_WLSysGive, SignBoon, JewelBoxBoon, LouFengConsumerRebate, ChessRechargePolite,
JiuGongGeDraw, CurrencyGive, GoldCouplePayVID, BookLoufeng, Active2023Cost, Active2023Reward, RaffleDeduction, PrizeRecord,
DailyTaskAdsClick, DailyTaskUserInvite, OnceTaskBuyVIP, OnceTaskBindMobile, ReceiveIntegral, BuyVIP, IntegralExchangeVip,
ReceiveIntegral, ProxyIncome, WorksIncome, SendMsgDebitIncomeGold, SendMsgDebitIncomeGoldReturn, SendMsgDebitAmountGold,
SendMsgDebitAmountGoldReturn, AiChangefaceDebitGold, AiChangefaceDebitGoldReturn, AiChangefaceDebitInComeGold, AiChangefaceDebitIncomeGoldReturn,
AiChangeFaceImgDebitGold, AiChangeFaceImgReturnGold, AiUndressDebitFreeTimes, AiUndressDebitFreeTimesReturn, AiUndressDebitIncomeGold,
AiUndressDebitIncomeGoldReturn, AiChangeFaceImgDebitIncomeGold, AiChangeFaceImgReturnIncomeGold, AiChangeFaceImgDebitFreeTimes,
AiChangeFaceImgDebitFreeTimesReturn, AiUndress, AiUndressRefund, AiUndressInc, AiUndressIncBackend, AiUndressDebitGold, AiUndressDebitGoldReturn,
VipCardGiveAiUndressFreeCount, AdminAddDownloadCount, AdminDebitDownloadCount, BuyAdvanceVIP, BuyBalanceVIP, BuyGameAdvanceVIP,
BuyWhoringCard, ReSignDebitAmount, SuccessSignReturnAmount, GodCommentAward, IntegralExchangeAICount, AiMateCurrencyExchange,
AiImageToVideoDebitGold, AiImageToVideoDebitGoldReturn, AiImageToVideoDebitInComeGold, AiImageToVideoDebitIncomeGoldReturn,
AiTextToImageDebitGold, AiTextToImageDebitGoldReturn, AiTextToImageDebitInComeGold, AiTextToImageDebitIncomeGoldReturn, BuyAcg,
AiTextToNovelDebitGold, AiTextToNovelDebitGoldReturn, AiTextToNovelDebitInComeGold, AiTextToNovelDebitIncomeGoldReturn,
}},
//"amount": bson.M{"$ne": 0},
"$or": []bson.M{
bson.M{"amount": bson.M{"$ne": 0}},
bson.M{"integral": bson.M{"$ne": 0}},
bson.M{"tranTypeInt": bson.M{"$in": []TransType{AiGirlfriendTransferIn, AiGirlfriendTransferOut}}},
},
}
if pageSize == 0 || pageSize >= uint64(maxLedgerWindow) {
return nil, false, fmt.Errorf("invalid ledger page size")
}
if pageNumebr == 0 {
pageNumebr = 1
}
if pageNumebr-1 > uint64(maxLedgerWindow)/pageSize {
return nil, false, fmt.Errorf("ledger query window is too large; narrow the time range")
}
rows, err := findLedgerRows(f, int64(pageSize*(pageNumebr-1)), int64(pageSize+1))
if err != nil {
return nil, false, err
}
hasNext = len(rows) > int(pageSize)
if hasNext {
rows = rows[:pageSize]
}
for _, row := range rows {
data = append(data, *row)
}
return data, hasNext, nil
}
// HasNextMonth 查询当前月之前的最迟数据的的时间
func HasNextMonth(uid uint64, start time.Time) (data TransactionLog, err error) {
opt := options.FindOne()
opt.Sort = bson.D{{Key: "createdAt", Value: -1}}
err = coll(nil).FindOne(&data, bson.M{"uid": uid, "createdAt": bson.M{"$lt": start}})
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "HasNext", table, "FindOne", err),
log.Any("uid", uid),
log.Any("start", start),
)
return
}
return
}
func BuyLoufengCount(uid uint64) (int64, error) {
return coll(nil).Count(bson.M{"uid": uid, "tranTypeInt": bson.M{"$in": []TransType{LouFeng, BookLoufeng}}})
}
// 时间内购买VIP次数
func BuyVipCount(start, end time.Time, mats ...Matcher) (int64, error) {
payVIP := PayVIP.Key()
mats = append(mats, (&TranTypeMatch{&payVIP}).New(), (&CreatedAtGTEAndLTMatch{GTE: &start, LT: &end}).New())
filter := pageopt.MergeM(mats)
count, err := coll(nil).Count(filter)
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "BuyVipCount", table, "Count", err))
}
return count, err
}
// //产品购买记录
func BuyProductLog(uid uint64, tranTypeInt TransType, pids []primitive.ObjectID) (data []TransactionLog, err error) {
objIDs := make([]string, len(pids))
for i := range pids {
objIDs[i] = pids[i].Hex()
}
data = make([]TransactionLog, 0)
opt := options.FindOptions{
Sort: bson.D{{Key: "createdAt", Value: -1}},
}
f := bson.M{"uid": uid, "tranTypeInt": tranTypeInt, "productID": bson.M{"$in": objIDs}}
if err = coll(nil).Find(&data, f, &opt); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "BuyProductLog", table, "Find", err))
return
}
return
}
// 产品购买记录 单个
func BuyProductLogSingle(uid uint64, tranTypeInt TransType, pid string) (data TransactionLog, err error) {
f := bson.M{"uid": uid, "tranTypeInt": tranTypeInt, "productID": pid}
if err = coll(nil).FindOne(&data, f); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "BuyProductLogSingle", table, "Find", err))
return
}
return
}
// 产品购买记录
func BuyProductLogWithPage(uid uint64, tranTypeInt TransType, page commod.Page) (data []TransactionLog, hasNext bool, err error) {
data = make([]TransactionLog, 0)
skip := int64(page.Skip())
limit := int64(page.Limit() + 1)
opts := options.FindOptions{
Skip: &skip,
Limit: &limit,
Sort: bson.D{{Key: "createdAt", Value: -1}},
}
f := bson.M{"uid": uid, "tranTypeInt": tranTypeInt}
if tranTypeInt == PayAvVID {
f["productID"] = bson.M{"$exists": true}
}
if err = coll(nil).Find(&data, f, &opts); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "BuyProductLog", table, "Find", err))
return
}
if uint64(len(data)) > page.PageSize {
hasNext = true
data = data[:page.PageSize]
}
return
}
// Income 账单
func WorksIncomebills(uid uint64, pageNumebr, pageSize uint64) (data []TransactionLog, hasNext bool, err error) {
data = make([]TransactionLog, 0)
f := bson.M{"uid": uid, "tranTypeInt": WorksIncome}
skip := int64(pageSize * (pageNumebr - 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.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "FindTransactionLogs", table, "Find", err),
log.Any("filter", f),
)
return
}
if len(data) > int(pageSize) {
hasNext = true
data = data[:pageSize]
}
return
}
// 是否购买了某个产品
func IsBuyProduct(uid uint64, id primitive.ObjectID, tranTypeInt TransType) (bool, error) {
f := bson.M{"uid": uid, "productID": id.Hex(), "tranTypeInt": tranTypeInt}
count, err := coll(nil).Count(f)
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "BuyVipCount", table, "Count", count))
return false, err
}
return count > 0, nil
}
// QueryAll 条件查询列表
func QueryAll(filter bson.M, opts ...*options.FindOptions) ([]TransactionLog, error) {
var items []TransactionLog
return items, coll(nil).Find(&items, filter, opts...)
}
// TransStat 交易统计
func TransStat(start, end time.Time) ([]TransactionR, error) {
var items []TransactionR
f := bson.M{"createdAt": bson.M{"$gt": start, "$lte": end}, "tranTypeInt": LouFeng}
return items, coll(nil).Find(&items, f)
}
// IncomeLeaderboard 收益榜单
func IncomeLeaderboard(bind interface{}, filter bson.M, limit int) error {
pip := []bson.M{
{"$match": filter},
{"$group": bson.M{"_id": "$uid", "income": bson.M{"$sum": "$actualAmount"}}},
{"$sort": bson.M{"income": -1}},
{"$limit": limit},
}
if err := coll(nil).Aggregate(bind, pip); err != nil {
log.Info(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "IncomeLeaderboard", table, "Aggregate", err))
return err
}
return nil
}
// FindByCond 根据条件查询
func FindByCond(filter primitive.M) (TransactionLog, error) {
opt := options.FindOneOptions{
Sort: bson.D{{Key: "createdAt", Value: -1}},
}
var data TransactionLog
if err := coll(nil).FindOne(&data, filter, &opt); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "LastTime", table, "FindByCond", err),
log.Any("filter", filter),
)
return data, err
}
return data, nil
}
// CheckRepurchaseByTransTypes 判断是否购买过,返回yes/no
func CheckRepurchaseByTransTypes(uid uint64, types []TransType) (isRepurchase string, err error) {
if len(types) == 0 {
return "no", nil
}
has, err := coll(nil).Exists(bson.M{"uid": uid, "tranTypeInt": bson.M{"$in": types}})
if err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "CheckRepurchaseByTransTypes", table, "Exists", err),
log.Any("uid", uid),
log.Any("types", types),
)
return "no", err
}
if has {
return "yes", nil
}
return "no", nil
}
+462
View File
@@ -0,0 +1,462 @@
package txnmod
import (
"time"
"91porn-server/common/db"
"91porn-server/models/commod"
"github.com/shopspring/decimal"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
)
var tranMap = map[TransType]string{
AiGirlfriendTransferIn: "AI女友上分",
AiGirlfriendTransferOut: "AI女友下分",
Rchg: "充值",
PayVID: "购买视频",
WithdrawTransfer: "提现转账",
PayVIP: "VIP购买",
WithdrawRefund: "提现失败退款",
WorksIncome: "作品收益",
ActIncome: "活动收益",
ProxyIncome: "代理收益",
AdminCreaditAmount: "官方增加金币",
AdminDebitAmount: "官方减去金币",
NengModel: "购买嫩模",
OfficialRech: "官方充值",
MeetingCard: "购买约会卡",
GameCoin: "购买游戏币",
ChaseScore: "官方追分",
RewardIncome: "打赏收益",
PayReward: "打赏",
CoinMonthCard: "金币月卡",
Other: "购买特殊卡",
LouFeng: "购买楼凤",
AudioBook: "购买有声小说",
LouFengRefund: "楼凤退款",
LouFengDiscount: "楼凤优惠卡",
GameRewards: "游戏奖励",
VipCardGive: "购买会员卡赠送金币",
LouFengMianFei: "楼凤全免卡",
LuckyDraw: "幸运抽奖",
PayAvVID: "购买AV解说视频",
AvCommentVideoIncome: "AV解说视频收益",
TranType_WLSysGive: "棋牌游戏活动赠送",
VipCardGiveGameCoin: "购买会员卡赠送游戏金币",
WaLiProxyIncome: "瓦力推广收益",
AppStoreRewardIncome: "应用中心下载奖励",
SignBoon: "签到福利",
JewelBoxBoon: "宝箱福利",
LouFengConsumerRebate: "消费返利(楼凤)",
ChessRechargePolite: "充值有礼(棋牌)",
JiuGongGeDraw: "九宫格抽奖",
NudeChatConsumption: "裸聊消费",
FruitCoinRecharge: "果币充值",
BuyNudeChatService: "购买裸聊服务",
NudeChatIncome: "裸聊收益",
CurrencyGive: "货币赠送",
BuyVIP: "充值购买会员卡",
NudeChatRefund: "裸聊退款",
AdminCreditFruitCoin: "官方增加果币",
AdminDebitFruitCoin: "官方减去果币",
VideoDiscount: "视频折扣卡",
GoldCouplePayVID: "金币抵用券购买视频",
VideoFreeCard: "视频免费卡",
Active2023Cost: "2023春节活动消耗金币",
Active2023Reward: "2023春节抽奖活动获得金币",
Active2023VIP: "2023春节抽奖活动获得VIP卡",
RaffleDeduction: "抽奖扣款记录",
PrizeRecord: "抽奖记录",
DailyTaskAdsClick: "每日广告下载任务",
DailyTaskUserInvite: "每日邀请任务",
OnceTaskBuyVIP: "新手任务: 购买VIP/金币",
OnceTaskBindMobile: "新手任务: 绑定手机",
AiUndress: "AI脱衣",
AiUndressRefund: "AI脱衣返还次数(后台未通过时返还次数)",
AiUndressInc: "AI脱衣新增次数",
AiUndressIncBackend: "AI脱衣后台赠送次数",
AiUndressDebitGold: "AI脱衣扣款金币记录",
AiUndressDebitGoldReturn: "AI脱衣扣款金币记录返还",
IntegralExchangeVip: "VIP",
DailyComment: "每日评论记录",
ReceiveIntegral: "领取积分",
GiveDownload: "赠送下载次数",
AiChangefaceDebitGold: "AI换脸扣款金币记录",
AiChangefaceDebitGoldReturn: "AI换脸扣款金币记录返还",
AiChangefaceDebitInComeGold: "AI换脸扣款收益金币记录",
AiChangefaceDebitIncomeGoldReturn: "AI换脸扣款收益金币记录返还",
SendMsgDebitIncomeGold: "用户发送私信扣除收益金币",
SendMsgDebitIncomeGoldReturn: "用户发送私信收益金币返还",
SendMsgDebitAmountGold: "用户发送私信扣除金币",
SendMsgDebitAmountGoldReturn: "用户发送私信扣除金币返还",
AdminAddAiUndressFreeTimes: "官方增加AI脱衣免费次数",
AdminDebitAiUndressFreeTimes: "官方减去AI脱衣免费次数",
AiChangeFaceImgDebitGold: "AI图片换脸金币扣款记录",
AiChangeFaceImgReturnGold: "AI图片换脸金币返还",
AiUndressDebitFreeTimes: "AI脱衣扣款免费次数记录",
AiUndressDebitFreeTimesReturn: "AI脱衣免费次数返回",
AiUndressDebitIncomeGold: "AI脱衣扣款收益金币记录",
AiUndressDebitIncomeGoldReturn: "AI脱衣收益金币返还",
AiChangeFaceImgDebitIncomeGold: "AI图片换脸收益金币扣款记录",
AiChangeFaceImgReturnIncomeGold: "AI图片换脸收益金币返还",
AiChangeFaceImgDebitFreeTimes: "AI图片换脸免费次数扣款记录",
AiChangeFaceImgDebitFreeTimesReturn: "AI图片换脸免费次数返回",
VipCardGiveAiUndressFreeCount: "购买会员卡赠送AI脱衣免费次数",
AdminAddVIP: "官方新增VIP",
AdminAddDownloadCount: "官方增加下载次数",
AdminDebitDownloadCount: "官方扣除加下载次数",
BuyAdvanceVIP: "充值购买预售卡",
BuyBalanceVIP: "充值购买预售尾卡",
BuyGameAdvanceVIP: "充值购买游戏预售卡",
AdminAddLotteryTimesCount: "官方增加抽奖次数",
AdminDebitLotteryTimesCount: "官方扣除加抽奖次数",
GiveLotteryTimesCount: "充值赠送抽奖次数",
IntegralExchangeInKind: "实物",
IntegralExchangeAICount: "AI黑科技券",
IntegralExchangeFreeCount: "幸运抽奖次数",
IntegralExchangeGoldCoinBonus: "金币加赠券",
IntegralExchangeGoldWatch: "金币观影券",
GodCommentAward: "神评奖励",
BuyWhoringCard: "购买白嫖卡",
ReSignDebitAmount: "补签打卡扣除金币",
SuccessSignReturnAmount: "完成打卡返回金币",
AiImageToVideoDebitGold: "AI图生视频金币扣款记录",
AiImageToVideoDebitGoldReturn: "AI图生视频金币返还",
AiImageToVideoDebitInComeGold: "AI图生视频收益金币扣款记录",
AiImageToVideoDebitIncomeGoldReturn: "AI图生视频收益金币返还",
AiTextToImageDebitGold: "AI绘图金币扣款记录",
AiTextToImageDebitGoldReturn: "AI绘图金币返还",
AiTextToImageDebitInComeGold: "AI绘图收益金币扣款记录",
AiTextToImageDebitIncomeGoldReturn: "AI绘图收益金币返还",
AiMateChat: "AI女友聊天花费积分",
AiMateCurrencyExchange: "AI伴侣货币兑换",
AdminAiMateSet: "官方设置AI伴侣货币值",
AiTextToNovelDebitGold: "AI小说金币扣款记录",
AiTextToNovelDebitGoldReturn: "AI小说金币返还",
AiTextToNovelDebitInComeGold: "AI小说收益金币扣款记录",
AiTextToNovelDebitIncomeGoldReturn: "AI小说收益金币返还",
AdminCreaditIntegral: "官方增加积分",
AdminDebitIntegral: "官方减去积分",
StoreBuyGoods: "商城购买普通商品",
StoreBuyNudeChat: "商城购买裸聊",
StorePublishWish: "商城发布许愿单",
StoreGoodsOrderRefund: "商城普通商品订单退款",
StoreNudeChatOrderRefund: "商城裸聊订单退款",
StoreWishRefund: "商城许愿单退款",
StoreWishEdit: "商城许愿单修改价格扣款",
BuyAcg: "购买ACG动漫",
JoinGroup: "加入群组",
BuyNakedChat: "购买裸聊",
OrderRefund: "官方充值订单退款",
OrderRefundVip: "官方充值VIP订单退款",
RefundVipCardGive: "官方充值VIP赠送金币退款",
ActivityRewardGold: "活动发放金币",
ActivityRewardVIP: "活动发放VIP",
ActivityRewardGoldBonusCoupon: "活动发放金币加赠券",
ActivityRewardGoldVideoCoupon: "活动发放金币观影券",
ActivityRewardAiChangeFaceFree: "活动发放AI换脸免费次数",
ActivityRewardAiUndressFree: "活动发放AI脱衣免费次数",
ActivityRewardIntegral: "活动发放积分",
ActivityRewardPhysical: "活动发放实物奖品",
ActivityDeductGold: "活动扣除金币",
ActivityDeductIntegral: "活动扣除积分",
ActivityDeductLotteryTimes: "活动扣除抽奖免费次数",
}
// TranType2ProductType 交易类型到产品类型映射
var TranType2ProductType = map[TransType]commod.ProductType{
PayVIP: commod.VIP,
MeetingCard: commod.MeetingCard,
Other: commod.OTHER,
BuyVIP: commod.VIP,
VideoDiscount: commod.VideoDiscount,
VideoFreeCard: commod.VideoFreeCard,
}
const (
Rchg TransType = iota + 1 // 充值
PayVID TransType = 2 // 购买视频
WithdrawTransfer TransType = 3 // 提现转账
PayVIP TransType = 4 // VIP购买(金币)
WithdrawRefund TransType = 5 // 提现失败退款
WorksIncome TransType = 6 // 作品收益
ActIncome TransType = 7 // 活动收益
ProxyIncome TransType = 8 // 代理收益
AdminCreaditAmount TransType = 9 // 官方增加金币
AdminDebitAmount TransType = 10 // 官方减去金币
NengModel TransType = 11 // 购买嫩模 弃用
OfficialRech TransType = 12 // 官方充值
MeetingCard TransType = 13 // 购买约会卡
GameCoin TransType = 14 // 购买游戏币
ChaseScore TransType = 15 // 从用户处追分,扣减用户金币
RewardIncome TransType = 16 // 打赏收益
PayReward TransType = 17 // 打赏
CoinMonthCard TransType = 18 // 金币月卡
Other TransType = 20 // 购买约会卡
LouFeng TransType = 21 // 购买楼凤联系方式
AudioBook TransType = 22 // 购买有声小说
LouFengRefund TransType = 23 // 楼凤退款(包含预约)
LouFengDiscount TransType = 24 // 楼凤优惠卡
GameRewards TransType = 25 // 游戏奖励
VipCardGive TransType = 26 // 购买会员卡赠送金币
LouFengMianFei TransType = 27 // 楼凤全免卡
LuckyDraw TransType = 28 // 幸运抽奖
PayAvVID TransType = 36 // 购买AV解说视频
AvCommentVideoIncome TransType = 37 // AV解说视频收益
TranType_WLSysGive TransType = 38 // 棋牌游戏活动赠送金币
VipCardGiveGameCoin TransType = 39 // 购买会员卡赠送棋牌金币
WaLiProxyIncome TransType = 40 // 瓦力代理收益
AppStoreRewardIncome TransType = 41 // 应用中心下载奖励
SignBoon TransType = 42 // 签到福利
JewelBoxBoon TransType = 43 // 宝箱福利
LouFengConsumerRebate TransType = 44 // 消费返利(楼凤)
ChessRechargePolite TransType = 45 // 充值有礼(棋牌)
JiuGongGeDraw TransType = 46 // 九宫格抽奖
NudeChatConsumption TransType = 47 // 裸聊消费
FruitCoinRecharge TransType = 48 // 果币充值
BuyNudeChatService TransType = 49 // 购买裸聊服务
NudeChatIncome TransType = 50 // 裸聊收益
CurrencyGive TransType = 51 // 货币赠送
BuyVIP TransType = 52 // 充值购买会员卡
NudeChatRefund TransType = 53 // 裸聊退款
AdminCreditFruitCoin TransType = 54 // 官方增加果币
AdminDebitFruitCoin TransType = 55 // 官方减去果币
VideoDiscount TransType = 56 // 视频折扣卡
VideoFreeCard TransType = 57 // 视频免费卡
GoldCouplePayVID TransType = 58 // 金币抵用券购买视频
BookLoufeng TransType = 59 // 预约
Active2023Cost TransType = 61 // 2023春节活动抽奖扣除金币
Active2023Reward TransType = 62 // 2023春节活动获得金币
Active2023VIP TransType = 63 // 2023新春抽奖获得VIP卡
RaffleDeduction TransType = 64 //抽奖扣款记录
PrizeRecord TransType = 65 //抽奖记录
AiUndressDebitGoldReturn TransType = 66 // AI脱衣金币返回
DailyTaskAdsClick TransType = 70 // 每日活动: 下载广告app
DailyTaskUserInvite TransType = 71 // 每日活动: 用户邀请
OnceTaskBuyVIP TransType = 80 // 一次性任务: 购买VIP
OnceTaskBindMobile TransType = 81 // 一次性任务: 绑定手机号
AiUndress TransType = 82 // AI脱衣
AiUndressRefund TransType = 83 // AI脱衣返还次数(后台未通过时返还次数)
AiUndressInc TransType = 84 // AI脱衣新增次数
AiUndressIncBackend TransType = 85 // AI脱衣后台赠送
AiUndressDebitGold TransType = 86 // AI脱衣金币扣款记录
IntegralExchangeVip TransType = 87 // 积分兑换VIP
DailyComment TransType = 88 // 每日评论记录
ReceiveIntegral TransType = 89 // 领取积分
GiveDownload TransType = 90 // 赠送下载次数
AiChangefaceDebitGold TransType = 91 // AI换脸金币扣款记录
AiChangefaceDebitGoldReturn TransType = 92 // AI换脸金币返还
AiChangefaceDebitInComeGold TransType = 93 // AI换脸收益金币扣款记录
AiChangefaceDebitIncomeGoldReturn TransType = 94 // AI换脸收益金币返还
SendMsgDebitIncomeGold TransType = 95 // 用户发送私信扣除收益金币
SendMsgDebitIncomeGoldReturn TransType = 96 // 用户发送私信收益金币返还
SendMsgDebitAmountGold TransType = 97 // 用户发送私信扣除金币
SendMsgDebitAmountGoldReturn TransType = 98 // 用户发送私信扣除金币返还
AdminAddAiUndressFreeTimes TransType = 99 // 官方增加AI脱衣免费次数
AdminDebitAiUndressFreeTimes TransType = 100 // 官方减去AI脱衣免费次数
AiChangeFaceImgDebitGold TransType = 101 // AI图片换脸金币扣款记录
AiChangeFaceImgReturnGold TransType = 102 // AI图片换脸金币返还
AiUndressDebitFreeTimes TransType = 103 // AI脱衣免费次数扣款记录
AiUndressDebitFreeTimesReturn TransType = 104 // AI脱衣免费次数返回
AiUndressDebitIncomeGold TransType = 105 // AI脱衣收益金币扣款记录
AiUndressDebitIncomeGoldReturn TransType = 106 // AI脱衣收益金币返回
AiChangeFaceImgDebitIncomeGold TransType = 107 // AI图片换脸收益金币扣款记录
AiChangeFaceImgReturnIncomeGold TransType = 108 // AI图片换脸收益金币返还
AiChangeFaceImgDebitFreeTimes TransType = 109 // AI图片换脸免费次数扣款记录
AiChangeFaceImgDebitFreeTimesReturn TransType = 110 // AI图片换脸免费次数返回
VipCardGiveAiUndressFreeCount TransType = 111 // 购买会员卡赠送AI脱衣免费次数
AdminAddVIP TransType = 112 // 官方新增VIP
AdminAddDownloadCount TransType = 113 // 官方增加下载次数
AdminDebitDownloadCount TransType = 114 // 官方扣除加下载次数
BuyAdvanceVIP TransType = 115 // 充值购买预售卡
BuyBalanceVIP TransType = 117 // 充值购买预售尾卡
BuyGameAdvanceVIP TransType = 116 // 充值购买游戏预售卡
AdminAddLotteryTimesCount TransType = 120 // 官方增加抽奖次数
AdminDebitLotteryTimesCount TransType = 121 // 官方扣除加抽奖次数
GiveLotteryTimesCount TransType = 122 // 赠送抽奖次数
BuyWhoringCard TransType = 123 // 充值购买白嫖卡
ReSignDebitAmount TransType = 124 // 补签打卡扣除金币
SuccessSignReturnAmount TransType = 125 // 完成打卡返回金币
IntegralExchangeInKind TransType = 126 // 积分兑换实物
IntegralExchangeAICount TransType = 127 // 积分兑换AI黑科技券
IntegralExchangeFreeCount TransType = 128 // 积分兑换幸运抽奖次数
IntegralExchangeGoldCoinBonus TransType = 129 // 积分兑换金币加赠券
IntegralExchangeGoldWatch TransType = 130 // 积分兑换金币观影券
GodCommentAward TransType = 131 // 神评奖励
OnceTaskTypeUserBuyCoin TransType = 132 // 一次性任务: 购买金币
AiMateCurrencyExchange TransType = 137 // AI伴侣货币兑换
AiMateChat TransType = 138 // AI女友聊天花费积分
AdminAiMateSet TransType = 139 // 官方设置AI伴侣货币值
AdminCreaditIntegral TransType = 143 // 官方增加积分
AdminDebitIntegral TransType = 144 // 官方减去积分
AiImageToVideoDebitGold TransType = 202 // AI图生视频金币扣款记录
AiImageToVideoDebitGoldReturn TransType = 203 // AI图生视频金币返还
AiImageToVideoDebitInComeGold TransType = 204 // AI图生视频收益金币扣款记录
AiImageToVideoDebitIncomeGoldReturn TransType = 205 // AI图生视频收益金币返还
AiTextToImageDebitGold TransType = 206 // AI绘图金币扣款记录
AiTextToImageDebitGoldReturn TransType = 207 // AI绘图金币返还
AiTextToImageDebitInComeGold TransType = 208 // AI绘图收益金币扣款记录
AiTextToImageDebitIncomeGoldReturn TransType = 209 // AI绘图收益金币返还
AiTextToNovelDebitGold TransType = 210 // AI小说金币扣款记录
AiTextToNovelDebitGoldReturn TransType = 211 // AI小说金币返还
AiTextToNovelDebitInComeGold TransType = 212 // AI小说收益金币扣款记录
AiTextToNovelDebitIncomeGoldReturn TransType = 213 // AI小说收益金币返还
BuyAcg TransType = 166 // 购买acg动漫
OrderRefund TransType = 301 // 官方充值退款
OrderRefundVip TransType = 302 // 官方充值VIP退款
RefundVipCardGive TransType = 303 // 官方充值VIP赠送金币退款
StoreBuyGoods TransType = 10001 // 商城购买普通商品
StoreBuyNudeChat TransType = 10002 // 商城购买裸聊
StorePublishWish TransType = 10003 // 商城发布许愿单
StoreGoodsOrderRefund TransType = 10004 // 商城普通商品订单退款
StoreNudeChatOrderRefund TransType = 10005 // 商城裸聊订单退款
StoreWishRefund TransType = 10006 // 商城许愿单退款
StoreWishEdit TransType = 10007 // 商城许愿单修改价格扣款
JoinGroup TransType = 10101 // 加入群组
BuyNakedChat TransType = 10201 // 购买裸聊
ActivityRewardGold TransType = 20001 // 活动发放金币
ActivityRewardVIP TransType = 20002 // 活动发放VIP
ActivityRewardGoldBonusCoupon TransType = 20003 // 活动发放金币加赠券
ActivityRewardGoldVideoCoupon TransType = 20004 // 活动发放金币观影券
ActivityRewardAiChangeFaceFree TransType = 20005 // 活动发放AI换脸免费次数
ActivityRewardAiUndressFree TransType = 20006 // 活动发放AI脱衣免费次数
ActivityRewardIntegral TransType = 20007 // 活动发放积分
ActivityRewardPhysical TransType = 20008 // 活动发放实物奖品
ActivityDeductGold TransType = 20101 // 活动扣除金币
ActivityDeductIntegral TransType = 20102 // 活动扣除积分
ActivityDeductLotteryTimes TransType = 20103 // 活动扣除抽奖免费次数
)
const (
CurrencyTypeGold = iota //金币
CurrencyTypeCash //现金
)
const (
AiGirlfriendTransferIn TransType = 145
AiGirlfriendTransferOut TransType = 146
)
type TransType int64
func (t TransType) Key() string {
if key, ok := tranMap[t]; ok {
return key
}
return ""
}
type M = bson.M
type DiscDoc = commod.DiscDoc
// TransactionLog 账户交易流水表,
type TransactionLog struct {
//流水id
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"`
//出金方 UID
UID uint64 `json:"uid" bson:"uid"`
//交易订单号
TransNo primitive.ObjectID `json:"purchaseOrder" bson:"purchaseOrder"`
//产品id 传购买某些物品或者卖出某些物品的id
ProductID *string `json:"productID" bson:"productID,omitempty"`
//金币
Amount int64 `json:"amount" bson:"amount"`
//积分
Integral int64 `json:"integral" bson:"integral"`
//用户实时余额
RealIntegral decimal.Decimal `json:"realIntegral" bson:"realIntegral"`
//实际收入/支出的积分
ActualIntegral float64 `json:"actualIntegral" bson:"actualIntegral"`
//实际收入/支出的金币
ActualAmount float64 `json:"actualAmount" bson:"actualAmount"`
//税率
Tax int64 `json:"tax" bson:"tax"`
//系统收取的税额 税率*定价
TaxAmount float64 `json:"taxAmount" bson:"taxAmount"`
//充值/提现渠道类型
ChannelType string `json:"channelType" bson:"channelType"`
//交易类型
TranType string `json:"tranType" bson:"tranType"`
//交易类型的数字值
TranTypeInt int64 `json:"tranTypeInt" bson:"tranTypeInt"`
//推广绩效
Performance int64 `form:"performance" json:"performance" bson:"performance"`
//充值用户
RechargeId uint64 `form:"rechargeId" json:"rechargeId" bson:"rechargeId"`
RechargeUser RechargeUserInfo `json:"rechargeUser" bson:"rechargeUser"` // 购买用户信息
//记录描述
Desc string `json:"desc" bson:"desc"`
//创建时间
CreatedAt time.Time `json:"createdAt" bson:"createdAt"`
//设备系统类型 ios pc android
SysType string `json:"sysType" bson:"sysType"`
//代理等级
AgentLevel int `form:"agentLevel" json:"agentLevel" bson:"agentLevel"`
//会员等级
VipLevel int `json:"vipLevel" bson:"vipLevel"`
//用户实时余额
RealAmount decimal.Decimal `json:"realAmount" bson:"realAmount"`
//棋牌充值/提现的金额 单位元
Money decimal.Decimal `json:"money" bson:"money"`
//用户瓦力棋牌游戏实时余额
WaLIRealAmount decimal.Decimal `json:"wlRealAmount" bson:"wlRealAmount"`
//唯一订单号
UniqueOrder string `json:"uniqueOrder,omitempty" bson:"uniqueOrder,omitempty"`
//交易类型(0 金币 1 现金)
CurrencyType int `json:"currencyType,omitempty" bson:"currencyType,omitempty"`
//果币
FruitCoin int64 `json:"fruitCoin" bson:"fruitCoin,omitempty"`
//下载次数
DownloadCount int64 `json:"downloadCount" bson:"downloadCount,omitempty"`
//抽奖次数
LotteryTimes int64 `json:"lotteryTimes" bson:"lotteryTimes,omitempty"`
//果币余额
FruitCoinBalance int64 `json:"fruitCoinBalance" bson:"fruitCoinBalance,omitempty"`
AiMatePoint float64 `json:"aiMatePoint" bson:"aiMatePoint,omitempty"` // AI女友积分
RealAiMatePoint float64 `json:"realAiMatePoint" bson:"realAiMatePoint,omitempty"` // 剩余AI女友积分
IsRepurchase string `json:"isRepurchase" bson:"isRepurchase"` // 是否复购,yes、no
DiscDoc `bson:",inline"`
}
type RechargeUserInfo struct {
UID uint64 `json:"uid" bson:"uid"` // 用户ID
Name string `json:"name" bson:"name"` // 用户姓名
Portrait string `json:"portrait" bson:"portrait"` // 用户头像
}
type AgentIncomeRes struct {
AgentLevel int `bson:"_id"`
TotalMoney float64 `bson:"totalAmount"`
TotalPerformance int64 `bson:"totalPerformance"`
}
type LoufengTemp struct {
ProductID string `json:"productID" bson:"productID,omitempty"` //产品id 传购买某些物品或者卖出某些物品的id
CreatedAt time.Time `json:"createdAt" bson:"createdAt"` //创建时间
}
type TransactionR struct {
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"` //流水id
ActualAmount float64 `json:"actualAmount" bson:"actualAmount"` //实际收入/支出的金币
CreatedAt time.Time `json:"createdAt" bson:"createdAt"` //创建时间
}
var mdb *db.MongoDB
func Init() {
mdb = db.Init(table)
initIndex()
}
+59
View File
@@ -0,0 +1,59 @@
package txnmod
import (
"time"
"91porn-server/models/commod"
)
// CoinLogReq 金币流水请求
type CoinLogReq struct {
UID uint64 `form:"uid" json:"uid"`
Balance int `form:"balance" json:"balance"` //0所有 1收入 2支出
TranType string `form:"tranType" json:"tranType"`
DistrictCode string `form:"districtCode" json:"districtCode"`
Start time.Time `form:"start" json:"start"`
End time.Time `form:"end" json:"end"`
commod.Page
}
// CoinLogResp 金币流水返回
type CoinLogResp struct {
Logs []*TransactionLog `json:"logs"`
Total int64 `json:"total"`
}
// TransactionQueryReq 账户交易流水表,
type TransactionQueryReq struct {
ID *string `json:"id,omitempty" bson:"_id,omitempty"` //流水id
UID *uint64 ` json:"uid,omitempty" bson:"uid,omitempty"` //出金方 UID
TransNo *string ` json:"transNo,omitempty" bson:"transNo,omitempty"` //交易订单号
Amount *int64 ` json:"amount,omitempty" bson:"amount,omitempty"` //金币
TransType *string ` json:"transType,omitempty" bson:"transType,omitempty"` //交易类型
Desc *string ` json:"desc,omitempty" bson:"desc,omitempty"` //记录描述
}
type BillsRes struct {
Month string `json:"month"`
Income int64 `json:"income"`
Withdraw int64 `json:"withdraw"`
IncomeStr string `json:"incomeStr"` //收益
WithdrawStr string `json:"withdrawStr"` //提现
List []*TransactionLog `json:"list"`
}
type Bills1Res struct {
//收益
Income string `json:"income"`
//支出
Expenditure string `json:"expenditure"`
HasNext bool `json:"hasNext"`
//列表
List []TransactionLog `json:"list"`
//当前焦点月之前是否有数据
HasNextMonth bool `json:"haxNextMonth"`
//当前焦点月有数据的最迟月份
Month int `json:"month"`
//当前焦点月有数据的最迟月份的年
Year int `json:"year"`
}