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
+7
View File
@@ -0,0 +1,7 @@
package walletmod
type WalletResp struct {
Wallet
// 当日剩余ai免费次数
TodayAiFreeTimes int64 `json:"todayAiFreeTimes" bson:"-"`
}
+113
View File
@@ -0,0 +1,113 @@
/*
* @Description:
* @Author: your name
* @Date: 2019-08-29 16:20:49
* @LastEditTime: 2019-08-30 18:53:18
* @LastEditors: Please set LastEditors
*/
package walletmod
import (
"91porn-server/common/db"
"time"
"github.com/shopspring/decimal"
"go.mongodb.org/mongo-driver/bson/primitive"
)
var mdb *db.MongoDB
// Wallet 钱包
type Wallet struct {
//钱包id 索引
ID primitive.ObjectID `form:"id" json:"id" bson:"_id,omitempty"`
//关联用户
UID uint64 `form:"uid" json:"uid" bson:"uid"`
//蝴蝶币数量
Amount int64 `form:"amount" json:"amount" bson:"amount"`
//冻结蝴蝶币数量(抽奖消耗)
FreezeAmount int64 `form:"freezeAmount" json:"freezeAmount" bson:"freezeAmount"`
//作品收益 (单位 个) 整数位
Income int64 `form:"income" json:"income" bson:"income"`
//作品收益 (单位 个) 例如:0.7 小数位
IncomePot float64 `form:"incomePot" json:"incomePot" bson:"incomePot"`
//累积充值金额
Consumption int64 `form:"consumption" json:"consumption" bson:"consumption"`
//收益金额(推广收益 单位 个)
Money int64 `form:"money" json:"money" bson:"money"`
//累积提取金额
TakeOut int64 `form:"takeOut" json:"takeOut" bson:"takeOut"`
//推广绩效
Performance int64 `form:"performance" json:"performance" bson:"performance"`
//视频收益
VidIncome float64 `json:"vidIncome" bson:"vidIncome"`
//推广收益
ProxyIncome float64 `json:"proxyIncome" bson:"proxyIncome"`
//推广用户累积充值金额,下级充值的,用来判断用户对应的分成区间,不做交易使用,单位分
ProxyConsumption int64 `form:"proxyConsumption" json:"proxyConsumption" bson:"proxyConsumption"`
//打赏收益
RewardIncome decimal.Decimal `json:"rewardIncome" bson:"rewardIncome"`
//累计扣除的收益金币数量
DedIncome decimal.Decimal `json:"dedIncome" bson:"dedIncome"`
//usdt可提现余额,防止用usdt充值,其它方式洗钱(usdt充多少提多少)
UsdtAmount int64 `form:"usdtAmount" json:"usdtAmount" bson:"usdtAmount"`
//瓦力推广收益
WaLiProxyIncome float64 `json:"waliProxyIncome" bson:"waliProxyIncome"`
//裸聊收益
NudeChatIncome decimal.Decimal `json:"nudeChatIncome" bson:"nudeChatIncome"`
//果币:裸聊专用币
FruitCoin int64 `json:"fruitCoin" bson:"fruitCoin"`
Integral int64 `json:"integral" bson:"integral"` // 积分
DownloadCount int64 `json:"downloadCount" bson:"downloadCount"` // 下载次数
AiUndressFreeTimes int64 `json:"aiUndressFreeTimes" bson:"aiUndressFreeTimes"` // 用户AI脱衣免费次数
TotalAiUndressFreeTimes int64 `json:"totalAiUndressFreeTimes" bson:"totalAiUndressFreeTimes"` // 用户累计AI脱衣免费次数
LotteryTimes int64 `json:"lotteryTimes" bson:"lotteryTimes"` // 用户抽奖次数
TotalLotteryTimes int64 `json:"totalLotteryTimes" bson:"totalLotteryTimes"` // 用户累计抽奖次数
AiMateBalance float64 `json:"aiMateBalance" bson:"aiMateBalance"` //ai伴侣余额
LastAiMateRecordId string `json:"lastAiMateRecordId" bson:"lastAiMateRecordId"` // 最后一条ai伴侣扣费记录id
DownloadRequests []DownloadRequest `json:"-" bson:"downloadRequests,omitempty"` // 最近下载授权幂等记录
}
type DownloadRequest struct {
RequestKey string `bson:"requestKey"`
Fingerprint string `bson:"fingerprint"`
CreatedAt time.Time `bson:"createdAt"`
}
func Init() {
mdb = db.Init(table)
initIndex()
}
type DebitPlan struct {
Amount int64
Income int64
AiUndressFreeTimes int64
Consumption int64 `bson:"consumption,omitempty"` // 累积充值金额
DownloadCount int64 `bson:"downloadCount,omitempty"` // 下载次数
LotteryTimes int64 `bson:"lotteryTimes,omitempty"` // 累积抽奖次数
}
type CreditPlan struct {
Amount *int64 `bson:"amount,omitempty"` // 金币
Income *int64 `bson:"income,omitempty"` // 收益
DownloadCount *int64 `bson:"downloadCount,omitempty"` // 下载次数
Performance *int64 `bson:"performance,omitempty"` // 推广绩效
VidIncome *float64 `bson:"vidIncome,omitempty"` // 视频收益
ProxyIncome *int64 `bson:"proxyIncome,omitempty"` // 推广收益
ProxyConsumption *int64 `bson:"proxyConsumption,omitempty"` // 推广用户累积充值金额
Consumption *int64 `bson:"consumption,omitempty"` // 累积充值金额
WaLiProxyIncome *float64 `bson:"waliProxyIncome,omitempty"` // 瓦力推广收益
FruitCoin *int64 `bson:"fruitCoin,omitempty"` // 果币
NudeChatIncome *decimal.Decimal `bson:"nudeChatIncome,omitempty"` // 裸聊收益
AiUndressFreeTimes *int64 `bson:"aiUndressFreeTimes,omitempty"` // 赠送AI脱衣次数
TotalAiUndressFreeTimes *int64 `bson:"totalAiUndressFreeTimes,omitempty"` // 累积AI脱衣次数
Integral *int64 `bson:"integral,omitempty"` // 积分
LotteryTimes *int64 `bson:"lotteryTimes,omitempty"` // 累积抽奖次数
AiMateBalance *float64 `bson:"aiMateBalance,omitempty"` // ai伴侣余额
}
type CreditPlanWaLi struct {
Income int64 `bson:"income,omitempty"` // 收益
WaLiProxyIncome float64 ` bson:"waliProxyIncome,omitempty"` // 瓦力推广收益
}
+943
View File
@@ -0,0 +1,943 @@
/*
* @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 walletmod
import (
"errors"
"fmt"
"sync"
"time"
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/models"
"github.com/shopspring/decimal"
"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 (
ErrDownloadRequestConflict = errors.New("download request id belongs to another resource")
ErrDownloadCountNotEnough = errors.New("download count is not enough")
ErrDownloadAuthorizationRace = errors.New("download authorization changed concurrently")
)
const maxDownloadRequestHistory = 1000
var mutex sync.Mutex
const table = models.Wallet
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: "uid", Value: 1}},
Options: options.Index().SetUnique(true),
},
}
if _, err := coll(nil).CreateIndex(many); err != nil {
panic(fmt.Sprintf("wallet model set index err ==>[%+v]", err))
}
}
func InsertWallet(t *db.MongoTool, w *Wallet) error {
if _, err := coll(t).InsertOne(w); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "InsertWallet", table, "InsertOne", err),
log.Any("w", w),
)
return err
}
return nil
}
// GetWallet 通过uid,查找用户余额
func GetWallet(uid uint64) (w *Wallet, err error) {
w = &Wallet{}
if err = coll(nil).FindOne(w, bson.M{"uid": uid}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "GetWallet", table, "FindOne", err),
log.Any("uid", uid),
)
}
return
}
func GetWalletMap(uidList []uint64) (map[uint64]Wallet, error) {
if len(uidList) == 0 {
return make(map[uint64]Wallet), nil
}
filter := bson.M{
"uid": bson.M{
"$in": uidList,
},
}
walletList := make([]Wallet, 0, len(uidList))
if err := coll(nil).Find(&walletList, filter); err != nil {
return nil, err
}
walletMap := make(map[uint64]Wallet, len(walletList))
for _, wallet := range walletList {
walletMap[wallet.UID] = wallet
}
return walletMap, nil
}
// CreditAmount 货币增加(虚拟货币)
func CreditAmount(t *db.MongoTool, amt int64, uid uint64) (*Wallet, error) {
w := Wallet{}
if err := coll(t).FindOneAndUpsert(&w, bson.M{"uid": uid}, bson.M{
"$inc": bson.M{"amount": amt},
}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "CreditAmount", table, "FindOneAndUpsert", err),
log.Any("amt", amt),
log.Any("uid", uid),
)
return nil, err
}
return &w, nil
}
// DebitAmount 货币扣除(虚拟货币)
func DebitAmount(t *db.MongoTool, amt int64, uid uint64) (*Wallet, error) {
mutex.Lock()
defer mutex.Unlock()
w := Wallet{}
if err := coll(t).FindOneAndUpdate(&w, bson.M{"uid": uid, "amount": bson.M{"$gte": amt}}, bson.M{
"$inc": bson.M{"amount": -amt}}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "DebitAmount", table, "FindOneAndUpdate", err),
log.Any("amt", amt),
log.Any("uid", uid),
)
return nil, err
}
if w.ID.IsZero() {
return nil, errors.New("not enough balance")
}
return &w, nil
}
// CreditIntegral 新增积分
func CreditIntegral(t *db.MongoTool, integral int64, uid uint64) (*Wallet, error) {
w := Wallet{}
if err := coll(t).FindOneAndUpsert(&w, bson.M{"uid": uid}, bson.M{
"$inc": bson.M{"integral": integral},
}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "CreditIntegral", table, "FindOneAndUpsert", err),
log.Any("integral", integral),
log.Any("uid", uid),
)
return nil, err
}
return &w, nil
}
// DebitIntegral 扣除积分
func DebitIntegral(t *db.MongoTool, integral int64, uid uint64) (*Wallet, error) {
mutex.Lock()
defer mutex.Unlock()
w := Wallet{}
if err := coll(t).FindOneAndUpdate(&w, bson.M{"uid": uid, "integral": bson.M{"$gte": integral}}, bson.M{
"$inc": bson.M{"integral": -integral}}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "DebitIntegral", table, "FindOneAndUpdate", err),
log.Any("integral", integral),
log.Any("uid", uid),
)
return nil, err
}
if w.ID.IsZero() {
return nil, errors.New("not enough balance")
}
return &w, nil
}
// 扣除金币
// 金币不足则报错
func DebitAmountAndCheck(t *db.MongoTool, uid int64, amt int64) error {
mutex.Lock()
defer mutex.Unlock()
result, err := coll(t).UpdateOne(bson.M{"uid": uid, "amount": bson.M{"$gte": amt}}, bson.M{
"$inc": bson.M{"amount": -amt}})
if err != nil {
return err
}
if result.ModifiedCount != 1 {
return errors.New("金币余额不足")
}
return nil
}
// 扣除金币
// 金币不足则报错
// 包括普通金币和可提现金币
// 先扣除普通金币, 再扣除可提现金币
// t 必须已经开启事务!!!
func DebitAmountDefault(t *db.MongoTool, uid int64, amt int64) error {
mutex.Lock()
defer mutex.Unlock()
var wallet Wallet
if err := coll(t).FindOne(&wallet, bson.M{"uid": uid}); err != nil {
return err
}
if wallet.Amount+wallet.Income < amt {
return errors.New("金币余额不足")
}
if wallet.Amount >= amt {
result, err := coll(t).UpdateOne(bson.M{"uid": uid, "amount": bson.M{"$gte": amt}}, bson.M{
"$inc": bson.M{"amount": -amt}})
if err != nil {
return err
}
if result.ModifiedCount != 1 {
return errors.New("金币余额不足")
}
return nil
}
useIncome := amt - wallet.Amount
result, err := coll(t).UpdateOne(bson.M{"uid": uid, "amount": wallet.Amount, "income": bson.M{"$gte": useIncome}}, bson.M{
"$inc": bson.M{"amount": -wallet.Amount, "income": -useIncome}})
if err != nil {
return err
}
if result.ModifiedCount != 1 {
return errors.New("金币余额不足")
}
return nil
}
// GetAmount 获取金币
func GetAmount(t *db.MongoTool, uid uint64) (int64, error) {
w := &Wallet{}
if err := coll(t).FindOne(w, bson.M{"uid": uid}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "GetAmount", table, "FindOne", err),
log.Any("uid", uid),
)
return 0, err
}
return w.Amount, nil
}
// GetAmount 获取果币
func GetFruitAmount(t *db.MongoTool, uid uint64) (int64, error) {
w := &Wallet{}
if err := coll(t).FindOne(w, bson.M{"uid": uid}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "GetAmount", table, "FindOne", err),
log.Any("uid", uid),
)
return 0, err
}
return w.FruitCoin, nil
}
// GetAiMateBalance 获取ai伴侣币余额
func GetAiMateBalance(t *db.MongoTool, uid uint64) (float64, error) {
w := &Wallet{}
if err := coll(t).FindOne(w, bson.M{"uid": uid}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "GetAmount", table, "FindOne", err),
log.Any("uid", uid),
)
return 0, err
}
return w.AiMateBalance, nil
}
// SetAmount 设置货币(虚拟货币)
func SetAmount(t *db.MongoTool, amt int64, uid uint64) error {
//写入数据库,待完成
if _, err := coll(t).UpdateOne(bson.M{"uid": uid}, bson.M{
"$set": bson.M{"amount": amt},
}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "SetAmount", table, "UpdateOne", err),
log.Any("amt", amt),
log.Any("uid", uid),
)
return err
}
return nil
}
// 返回更新前的值 被删除的商人无法更新
func FindUpdate(tool *db.MongoTool, uid uint64, amt int64) (*Wallet, error) {
w := Wallet{}
opt := &options.FindOneAndUpdateOptions{}
opt.SetReturnDocument(options.Before)
if err := coll(tool).FindOneAndUpdate(&w, bson.M{"uid": uid}, bson.M{"$set": bson.M{"amount": amt}}, opt); err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "FindUpdate", table, "FindOneAndUpdate", err))
return nil, err
}
if w.ID.IsZero() {
return nil, errors.New("wallet not found")
}
return &w, nil
}
func Debit(t *db.MongoTool, p *DebitPlan, uid uint64) (*Wallet, error) {
if p == nil {
return nil, errors.New("wallet nil DebitPlan")
}
w := &Wallet{}
var err error
if p.Amount != 0 {
w, err = DebitAmount(t, p.Amount, uid)
if err != nil {
return nil, err
}
}
if p.Income != 0 {
w, err = DebitIncome(t, p.Income, uid)
if err != nil {
return nil, err
}
}
if p.Consumption != 0 {
err = CreditConsumption(t, p.Consumption, uid)
if err != nil {
return nil, err
}
}
if p.DownloadCount != 0 {
err = CreditDownloadCount(t, p.DownloadCount, uid)
if err != nil {
return nil, err
}
}
if p.AiUndressFreeTimes != 0 {
w, err = DebitAiFreeTimes(t, p.AiUndressFreeTimes, uid)
if err != nil {
return nil, err
}
}
if p.LotteryTimes != 0 {
w, err = DebitLotteryTimes(t, p.LotteryTimes, uid)
if err != nil {
return nil, err
}
}
if !w.ID.IsZero() {
return w, nil
}
if err = coll(t).FindOne(w, bson.M{"uid": uid}); err != nil {
return nil, err
}
if w.ID.IsZero() {
return nil, errors.New("wallet not found")
}
return w, nil
}
// CreditIncomeBasePot
func CreditIncomeBasePot(t *db.MongoTool, incomeInt int64, income float64, pot float64, uid uint64) (*Wallet, error) {
mutex.Lock()
defer mutex.Unlock()
var wallet Wallet
if err := coll(t).FindOneAndUpsert(&wallet, bson.M{"uid": uid}, bson.M{
"$set": bson.M{"incomePot": pot, "vidIncome": income},
"$inc": bson.M{"income": incomeInt},
}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "CreditIncome", table, "FindOneAndUpdate", err),
log.Any("income", income),
log.Any("incomePot", pot),
log.Any("uid", uid),
)
return nil, err
}
return &wallet, nil
}
// CreditIncomeBasePotWithReward
func CreditIncomeBasePotWithReward(t *db.MongoTool, incomeInt int64, rewardIncome decimal.Decimal, pot float64, uid uint64) (*Wallet, error) {
var w Wallet
if err := coll(t).FindOneAndUpsert(&w, bson.M{"uid": uid}, bson.M{
"$set": bson.M{"incomePot": pot},
"$inc": bson.M{"income": incomeInt, "rewardIncome": rewardIncome},
}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "CreditIncome", table, "FindOneAndUpsert", err),
log.Any("income", incomeInt),
log.Any("incomePot", pot),
log.Any("uid", uid),
)
return nil, err
}
return &w, nil
}
// CreditDebIncome 累计扣量金币 增加
func CreditDebIncome(t *db.MongoTool, income string, uid uint64) error {
debIncr, _ := primitive.ParseDecimal128(income)
//写入数据库,待完成
res, err := coll(t).UpsertOne(bson.M{"uid": uid}, bson.M{
"$inc": bson.M{"dedIncome": debIncr},
})
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "CreditDebIncome", table, "UpsertOne", err),
log.Any("income", income),
log.Any("uid", uid),
)
return err
}
if res.UpsertedCount != 1 && res.ModifiedCount != 1 {
return errors.New("Invalid account")
}
return nil
}
// CreditIncome 货币增加
func CreditIncome(t *db.MongoTool, income int64, uid uint64) (*Wallet, error) {
var w Wallet
if err := coll(t).FindOneAndUpsert(&w, bson.M{"uid": uid}, bson.M{
"$inc": bson.M{"income": income},
}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "CreditIncome", table, "FindOneAndUpsert", err),
log.Any("income", income),
log.Any("uid", uid),
)
return nil, err
}
return &w, nil
}
// DebitIncome 货币扣除
func DebitIncome(t *db.MongoTool, income int64, uid uint64) (*Wallet, error) {
var w Wallet
if err := coll(t).FindOneAndUpdate(&w, bson.M{"uid": uid, "income": bson.M{"$gte": income}}, bson.M{
"$inc": bson.M{"income": -income},
}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "DebitIncome", table, "FindOneAndUpdate", err),
log.Any("income", income),
log.Any("uid", uid),
)
return nil, err
}
if w.ID.IsZero() {
return nil, errors.New("wallet not found")
}
return &w, nil
}
// CreditFruitCoin 果币增加
func CreditFruitCoin(t *db.MongoTool, uid uint64, fruitCoin int64) (*Wallet, error) {
var item *Wallet
if err := coll(t).FindOneAndUpsert(&item, bson.M{"uid": uid}, bson.M{"$inc": bson.M{"fruitCoin": fruitCoin}}); err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "CreditFruitCoin", table, "FindOneAndUpsert", err),
log.Any("fruitCoin", fruitCoin),
log.Any("uid", uid),
)
return nil, err
}
return item, nil
}
// UpdateAiMateBalance ai伴侣币修改
func UpdateAiMateBalance(t *db.MongoTool, uid uint64, aiMateBalance float64) (*Wallet, error) {
var item *Wallet
if err := coll(t).FindOneAndUpsert(&item, bson.M{"uid": uid}, bson.M{"$set": bson.M{"aiMateBalance": aiMateBalance}}); err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "UpdateAiMateBalance", table, "FindOneAndUpsert", err),
log.Any("aiMateBalance", aiMateBalance),
log.Any("uid", uid),
)
return nil, err
}
return item, nil
}
// DebitFruitCoin 果币减少
func DebitFruitCoin(t *db.MongoTool, uid uint64, fruitCoin int64) (*Wallet, error) {
var item *Wallet
if err := coll(t).FindOneAndUpsert(&item, bson.M{"uid": uid}, bson.M{"$inc": bson.M{"fruitCoin": -fruitCoin}}); err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "DebitFruitCoin", table, "FindOneAndUpsert", err),
log.Any("fruitCoin", -fruitCoin),
log.Any("uid", uid),
)
return nil, err
}
return item, nil
}
// CreditNudeChatIncome 裸聊收益增加
func CreditNudeChatIncome(t *db.MongoTool, uid uint64, nudeChatIncome int64) (*Wallet, error) {
var item *Wallet
if err := coll(t).FindOneAndUpsert(&item, bson.M{"uid": uid}, bson.M{"$inc": bson.M{
"nudeChatIncome": decimal.NewFromInt(nudeChatIncome),
"income": nudeChatIncome,
}}); err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "CreditNudeChatIncome", table, "FindOneAndUpsert", err),
log.Any("nudeChatIncome", nudeChatIncome),
log.Any("uid", uid),
)
return nil, err
}
return item, nil
}
// DebitNudeChatIncome 裸聊收益减少
func DebitNudeChatIncome(t *db.MongoTool, uid uint64, nudeChatIncome int64) (*Wallet, error) {
var item *Wallet
if err := coll(t).FindOneAndUpsert(&item, bson.M{"uid": uid}, bson.M{"$inc": bson.M{
"nudeChatIncome": decimal.NewFromInt(-nudeChatIncome),
"income": -nudeChatIncome,
}}); err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "DebitNudeChatIncome", table, "FindOneAndUpsert", err),
log.Any("nudeChatIncome", nudeChatIncome),
log.Any("uid", uid),
)
return nil, err
}
return item, nil
}
// CreditMoney 货币增加
func CreditMoney(t *db.MongoTool, money int64, uid uint64) error {
//写入数据库,待完成
res, err := coll(t).UpsertOne(bson.M{"uid": uid}, bson.M{
"$inc": bson.M{"money": money},
})
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "CreditMoney", table, "UpsertOne", err),
log.Any("money", money),
log.Any("uid", uid),
)
return err
}
if res.UpsertedCount != 1 && res.ModifiedCount != 1 {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "CreditMoney", table, "errors.New", "Invalid account"),
log.Any("money", money),
log.Any("uid", uid),
)
return errors.New("Invalid account")
}
return nil
}
// DebitMoney 货币扣除
func DebitMoney(t *db.MongoTool, money int64, uid uint64) (*Wallet, error) {
w := Wallet{}
if err := coll(t).FindOneAndUpdate(&w, bson.M{"uid": uid, "money": bson.M{"$gte": money}}, bson.M{
"$inc": bson.M{"money": -money},
}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "DebitMoney", table, "FindOneAndUpdate", err),
log.Any("money", money),
log.Any("uid", uid),
)
return nil, err
}
if w.ID.IsZero() {
return nil, errors.New("not enough balance")
}
return &w, nil
}
// CreditConsumption 总充值增加
func CreditConsumption(t *db.MongoTool, money int64, uid uint64) error {
res, err := coll(t).UpsertOne(bson.M{
"uid": uid,
}, bson.M{
"$inc": bson.M{
"consumption": money,
},
})
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "CreditConsumption", table, "UpsertOne", err),
log.Any("money", money),
log.Any("uid", uid),
)
return err
}
if res.UpsertedCount != 1 && res.ModifiedCount != 1 {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "CreditConsumption", table, "errors.New", "Invalid account"),
log.Any("money", money),
log.Any("uid", uid),
)
return errors.New("Invalid account")
}
return nil
}
// CreditDownloadCount 总下载次数加
func CreditDownloadCount(t *db.MongoTool, downloadCount int64, uid uint64) error {
res, err := coll(t).UpsertOne(bson.M{"uid": uid}, bson.M{"$inc": bson.M{"downloadCount": -downloadCount}})
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "CreditDownloadCount", table, "UpsertOne", err),
log.Any("downloadCount", downloadCount),
log.Any("uid", uid),
)
return err
}
if res.UpsertedCount != 1 && res.ModifiedCount != 1 {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "CreditDownloadCount", table, "errors.New", "Invalid account"),
log.Any("downloadCount", downloadCount),
log.Any("uid", uid),
)
return errors.New("update DownloadCount err")
}
return nil
}
func Find(filter bson.M) ([]*Wallet, error) {
ws := make([]*Wallet, 0)
if err := coll(nil).Find(&ws, filter); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "Find", table, "Find", err),
log.Any("filter", filter),
)
return nil, err
}
return ws, nil
}
// Credit 货币增加(虚拟货币)
func Credit(t *db.MongoTool, creditPlan CreditPlan, uid uint64) (*Wallet, error) {
w := Wallet{}
if err := coll(t).FindOneAndUpsert(&w, bson.M{"uid": uid}, bson.M{
"$inc": creditPlan,
}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "CreditAmount", table, "FindOneAndUpsert", err),
log.Any("creditPlan", creditPlan),
log.Any("uid", uid),
)
return nil, err
}
return &w, nil
}
// DescCredit 货币扣除(虚拟货币)
func DescCredit(t *db.MongoTool, creditPlan CreditPlan, uid uint64) (wallet *Wallet, err error) {
filter := bson.M{"uid": uid}
if creditPlan.Income != nil {
filter["income"] = bson.M{"$gte": creditPlan.Income}
}
if creditPlan.Amount != nil {
filter["amount"] = bson.M{"$gte": creditPlan.Amount}
}
if creditPlan.LotteryTimes != nil {
filter["lotteryTimes"] = bson.M{"$gte": creditPlan.LotteryTimes}
}
err = coll(t).FindOneAndUpsert(&wallet, filter, bson.M{
"$inc": creditPlan,
})
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "DescCredit", table, "FindOneAndUpsert", err),
log.Any("creditPlan", creditPlan),
log.Any("uid", uid),
)
}
return
}
// 抽奖预扣
func AddFreeze(userId uint64, amt int64) (*Wallet, error) {
filter := bson.M{
"uid": userId,
"amount": bson.M{"$gte": amt},
}
inc := bson.M{
"amount": -amt,
"freezeAmount": amt,
}
w := Wallet{}
if err := coll(nil).FindOneAndUpdate(&w, filter, bson.M{"$inc": inc}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "DrawFreeze", table, "FindOneAndUpdate", err),
log.Any("uid", userId),
log.Any("amt", amt),
)
return nil, err
}
if w.ID.IsZero() {
return nil, errors.New("not enough balance")
}
return &w, nil
}
// 扣除预扣
func CutFreeze(t *db.MongoTool, userId uint64, amt, amount int64) (*Wallet, error) {
filter := bson.M{
"uid": userId,
"freezeAmount": bson.M{"$gte": amt},
}
inc := bson.M{
"freezeAmount": -amt,
"amount": amount,
}
w := Wallet{}
if err := coll(nil).FindOneAndUpdate(&w, filter, bson.M{"$inc": inc}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "CutFreeze", table, "FindOneAndUpdate", err),
log.Any("uid", userId),
log.Any("amt", amt),
)
return nil, err
}
if w.ID.IsZero() {
return nil, errors.New("not enough balance")
}
return &w, nil
}
func (w *Wallet) RealAmount() decimal.Decimal {
return decimal.NewFromInt(w.Amount).Add(decimal.NewFromInt(w.Income)).
Add(decimal.NewFromFloat(w.IncomePot)).Add(decimal.NewFromInt(w.Money))
}
func GetRealAmount(w *Wallet) decimal.Decimal {
if w == nil {
return decimal.Decimal{}
}
return decimal.NewFromInt(w.Amount).Add(decimal.NewFromInt(w.Income)).
Add(decimal.NewFromFloat(w.IncomePot)).Add(decimal.NewFromInt(w.Money))
}
func (w *Wallet) RealIntegral() decimal.Decimal {
if w != nil && w.Integral != 0 {
return decimal.NewFromInt(w.Integral)
} else {
return decimal.NewFromInt(0)
}
}
func UpsertOne(t *db.MongoTool, filter bson.M, update bson.M) error {
if _, err := coll(t).UpsertOne(filter, update); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "update", table, "updateOne", err))
return err
}
return nil
}
func BulkWrite(t *db.MongoTool, models []mongo.WriteModel, opts ...*options.BulkWriteOptions) error {
if _, err := coll(t).Bulk(models, opts...); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "update", table, "updateOne", err))
return err
}
return nil
}
// IncUsdtAmount usdt可提现额度加减
func IncUsdtAmount(userId uint64, usdtAmount int64) (*Wallet, error) {
filter := bson.M{"uid": userId}
if usdtAmount < 0 {
filter["usdtAmount"] = bson.M{"$gte": 0}
}
inc := bson.M{"usdtAmount": usdtAmount}
var w Wallet
if err := coll(nil).FindOneAndUpdate(&w, filter, bson.M{"$inc": inc}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "IncUsdtAmount", table, "FindOneAndUpdate", err),
log.Any("uid", userId),
log.Any("amt", usdtAmount),
)
return nil, err
}
if w.ID.IsZero() {
return nil, errors.New("wallet not found")
}
return &w, nil
}
// AddUsdtAmount 增加usdt可提现额度
func AddUsdtAmount(t *db.MongoTool, userId uint64, usdtAmount int64) error {
result, err := coll(t).UpdateOne(bson.M{"uid": userId}, bson.M{"$inc": bson.M{"usdtAmount": usdtAmount}})
if err != nil {
return err
}
if result.ModifiedCount == 0 {
return errors.New("result.ModifiedCount is 0")
}
return nil
}
// IsRechargeUser 是否充值用户
func IsRechargeUser(t *db.MongoTool, uid uint64) bool {
w := &Wallet{}
if err := coll(t).FindOne(w, bson.M{"uid": uid}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "IsRechargeUser", table, "FindOne", err),
log.Any("uid", uid),
)
return false
}
if w.ID.IsZero() || w.Consumption <= 0 {
return false
}
return true
}
// DebitAmountAndIncome 货币扣除(虚拟货币)
func DebitAmountAndIncome(t *db.MongoTool, amt, income int64, uid uint64) (wallet *Wallet, err error) {
mutex.Lock()
defer mutex.Unlock()
filter := bson.M{"uid": uid}
update := bson.M{}
if amt > 0 {
filter["amount"] = bson.M{"$gte": amt}
update["amount"] = -amt
}
if income > 0 {
filter["income"] = bson.M{"$gte": income}
update["income"] = -income
}
err = coll(t).FindOneAndUpdate(&wallet, filter, bson.M{"$inc": update})
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "DebitAmountAndIncome", table, "FindOneAndUpdate", err),
log.Any("filter", filter),
log.Any("update", update),
log.Any("uid", uid),
)
}
return
}
// ReturnAmountAndIncome 货币返回(虚拟货币)
func ReturnAmountAndIncome(t *db.MongoTool, amt, income int64, uid uint64) (wallet *Wallet, err error) {
mutex.Lock()
defer mutex.Unlock()
filter := bson.M{"uid": uid}
update := bson.M{}
if -amt > 0 {
update["amount"] = -amt
}
if -income > 0 {
update["income"] = -income
}
err = coll(t).FindOneAndUpdate(&wallet, filter, bson.M{"$inc": update})
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "ReturnAmountAndIncome", table, "FindOneAndUpdate", err),
log.Any("filter", filter),
log.Any("update", update),
log.Any("uid", uid),
)
}
return
}
// DebitAiFreeTimes 扣出AI脱衣次数
func DebitAiFreeTimes(t *db.MongoTool, freeTimes int64, uid uint64) (wallet *Wallet, err error) {
filter := bson.M{"uid": uid}
if freeTimes > 0 {
filter["aiUndressFreeTimes"] = bson.M{"$gte": freeTimes}
}
err = coll(t).FindOneAndUpsert(&wallet, filter, bson.M{"$inc": bson.M{"aiUndressFreeTimes": -freeTimes}})
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "DebitAiFreeTimes", table, "FindOneAndUpdate", err),
log.Any("freeTimes", freeTimes),
log.Any("uid", uid),
)
}
return
}
// DebitDownloadCounts 扣出下载次数
func DebitDownloadCounts(t *db.MongoTool, downloadCount int64, uid uint64) (wallet *Wallet, err error) {
filter := bson.M{"uid": uid}
if downloadCount > 0 {
filter["downloadCount"] = bson.M{"$gte": downloadCount}
}
err = coll(t).FindOneAndUpsert(&wallet, filter, bson.M{"$inc": bson.M{"downloadCount": -downloadCount}})
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "DebitAiDownloadCount", table, "FindOneAndUpdate", err),
log.Any("downloadCount", downloadCount),
log.Any("uid", uid),
)
}
return
}
// AuthorizeDramaDownload atomically records one idempotency key and debits one
// shared download count. The bounded wallet history covers retries even if an
// App instance or Redis cache restarts.
func AuthorizeDramaDownload(t *db.MongoTool, uid uint64, requestKey, fingerprint string, now time.Time) (*Wallet, bool, error) {
wallet := &Wallet{}
if err := coll(t).FindOne(wallet, bson.M{"uid": uid}); err != nil {
return nil, false, err
}
for _, request := range wallet.DownloadRequests {
if request.RequestKey != requestKey {
continue
}
if request.Fingerprint != fingerprint {
return nil, false, ErrDownloadRequestConflict
}
return wallet, false, nil
}
if wallet.DownloadCount <= 0 {
return nil, false, ErrDownloadCountNotEnough
}
record := DownloadRequest{RequestKey: requestKey, Fingerprint: fingerprint, CreatedAt: now}
filter := bson.M{
"uid": uid, "downloadCount": bson.M{"$gte": 1},
"downloadRequests.requestKey": bson.M{"$ne": requestKey},
}
update := bson.M{
"$inc": bson.M{"downloadCount": -1},
"$push": bson.M{"downloadRequests": bson.M{
"$each": []DownloadRequest{record}, "$slice": -maxDownloadRequestHistory,
}},
}
updated := &Wallet{}
if err := coll(t).FindOneAndUpdate(updated, filter, update); err != nil {
return nil, false, err
}
if updated.UID == 0 {
return nil, false, ErrDownloadAuthorizationRace
}
return updated, true, nil
}
// DebitLotteryTimes 扣出抽奖次数
func DebitLotteryTimes(t *db.MongoTool, lotteryTimes int64, uid uint64) (wallet *Wallet, err error) {
filter := bson.M{"uid": uid}
if lotteryTimes > 0 {
filter["lotteryTimes"] = bson.M{"$gte": lotteryTimes}
}
err = coll(t).FindOneAndUpsert(&wallet, filter, bson.M{"$inc": bson.M{"lotteryTimes": -lotteryTimes}})
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "DebitAiDownloadCount", table, "FindOneAndUpdate", err),
log.Any("downloadCount", lotteryTimes),
log.Any("uid", uid),
)
}
return
}
// Credit ai伴侣货币增加(虚拟货币)
func CreditAiMate(t *db.MongoTool, amt float64, uid uint64, aiMateOrderId string) (wallet *Wallet, err error) {
update := bson.M{"$inc": bson.M{"aiMateBalance": amt}}
if amt < 0 && aiMateOrderId != "" {
update["$set"] = bson.M{"lastAiMateRecordId": aiMateOrderId}
}
if err = coll(t).FindOneAndUpsert(&wallet, bson.M{"uid": uid}, update); err != nil {
log.Warn(fmt.Sprintf("[METHOD-CreditAmount]==> Model %s FindOneAndUpsert fail error:%+v:", table, err),
log.Any("aiMateBalance", amt),
log.Any("uid", uid),
)
}
return
}
// 重新设置用户ai积分
func SetAiMateBalance(t *db.MongoTool, uid uint64, aiMateBalance float64) error {
mutex.Lock()
defer mutex.Unlock()
_, err := coll(t).UpsertOne(bson.M{"uid": uid}, bson.M{"$set": bson.M{"aiMateBalance": aiMateBalance}})
return err
}
// GetIntegral 获取积分
func GetIntegral(t *db.MongoTool, uid uint64) (int64, error) {
w := &Wallet{}
if err := coll(t).FindOne(w, bson.M{"uid": uid}); err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "GetAmount", table, "FindOne", err),
log.Any("uid", uid),
)
return 0, err
}
return w.Integral, nil
}