/* * @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 }