package service import ( "91porn-server/models/v/advanceordermod" "91porn-server/models/v/taskmod" "context" "errors" "fmt" "strconv" "sync" "time" "91porn-server/app/service/productser" "91porn-server/app/service/taskser" "91porn-server/common" "91porn-server/common/db" "91porn-server/common/log" "91porn-server/models/commod" "91porn-server/models/v/currencymod" "91porn-server/models/v/goldextramod" "91porn-server/models/v/oncetaskmod" "91porn-server/models/v/productmod" "91porn-server/models/v/rchgordmod" "91porn-server/models/v/txnmod" "91porn-server/models/v/usermod" "91porn-server/models/v/walletmod" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" ) // BuyGold 购买金币 payMoney单位为分 func BuyGold(ctx context.Context, order rchgordmod.RechargeOrder, payMoney int64) (func(*db.MongoTool) error, int64, error) { var gold = payMoney / 10 // 查询金币配置 currency, err := currencymod.Get(order.ProductID) if err != nil { log.ErrorX(ctx, fmt.Sprintf("充值金币-订单号[%s] 产品ID[%v] 查询异常[%v]", order.ID, order.ProductID, err)) return nil, 0, err } // 未匹配到对应配置 if currency.ID.IsZero() { log.ErrorX(ctx, fmt.Sprintf("充值金币-订单号[%s] 产品ID[%v] 不存在", order.ID, order.ProductID)) } else { // 匹配到对应配置 // 金额不能低于配置金额1元并且有赠送金额 if (payMoney+100-currency.Price) > 0 && currency.GiveGold > 0 { gold += currency.GiveGold } } return func(t *db.MongoTool) error { // 增加金币与总充值金额 originGold := gold goldExtra := uint64(0) if order.GoldExtraID != nil { extra, err := goldextramod.GetUserGoldExtraByID(t, *order.GoldExtraID) if err != nil { log.ErrorX(ctx, fmt.Sprintf("充值金币-订单号[%s] 查询金币加购券[%v] 出错: %s", order.ID, *order.GoldExtraID, err.Error())) return err } if extra.Uid != order.UID || extra.Used || extra.Expired.Before(time.Now()) { log.ErrorX(ctx, fmt.Sprintf("充值金币-订单号[%s] 查询金币加购券[%v] 无效", order.ID, *order.GoldExtraID)) return errors.New("无效的金币加购券") } if err := goldextramod.UseGoldExtra(t, order.UID, *order.GoldExtraID); err != nil { log.ErrorX(ctx, fmt.Sprintf("充值金币-订单号[%s] 使用金币加购券[%v] 出错: %s", order.ID, *order.GoldExtraID, err.Error())) return err } goldExtra = extra.Amount gold += int64(extra.Amount) } w, err := walletmod.Credit(t, walletmod.CreditPlan{ Amount: &gold, Consumption: &payMoney, }, order.UID) if err != nil { return err } // 完成一次性任务 //if err = taskser.CompleteOnceTask(t, order.UID, oncetaskmod.OnceTaskTypeUserBuyCoin); err != nil { // fmt.Println("buy coin complete once task err", order.UID, err.Error()) // return err //} desc := "官方充值-到账" + strconv.FormatInt(gold, 10) + "金币" if goldExtra != 0 { desc = "官方充值-到账" + strconv.FormatInt(originGold, 10) + "金币, 消耗金币加购券增加" + strconv.FormatUint(goldExtra, 10) + "金币" } err = txnmod.InsertTransactionLog(t, &txnmod.TransactionLog{ TransNo: order.ID, UID: order.UID, Amount: gold, ActualAmount: float64(gold), TranType: txnmod.OfficialRech.Key(), TranTypeInt: int64(txnmod.OfficialRech), ChannelType: order.RechargeType, Desc: desc, DiscDoc: order.DiscDoc, SysType: order.DevType, RealAmount: w.RealAmount(), }) if err != nil { return err } common.Go(func() { // 完成任务 _ = taskser.CompleteOnceTask(nil, order.UID, oncetaskmod.OnceTaskTypeUserBuyCoin) taskser.CompleteGrowthTask(order.UID, taskmod.GrowthBuyGold, w.Consumption) }) return nil }, gold, nil } // BuyFruitCoin 购买果币 func BuyFruitCoin(ctx context.Context, order rchgordmod.RechargeOrder, payMoney int64) (func(*db.MongoTool) error, error) { fruitCoin := payMoney / 100 // 查询货币币配置 currency, err := currencymod.Get(order.ProductID) if err != nil { log.ErrorX(ctx, fmt.Sprintf("充值金币-订单号[%s] 产品ID[%v] 查询异常[%v]", order.ID, order.ProductID, err)) return nil, err } // 未匹配到对应配置 if currency.ID.IsZero() { log.ErrorX(ctx, fmt.Sprintf("充值金币-订单号[%s] 产品ID[%v] 不存在", order.ID, order.ProductID)) } else { // 匹配到对应配置 // 金额不能低于配置金额1元并且有赠送金额 if (payMoney+100-currency.Price) > 0 && currency.GiveFruitCoin > 0 { fruitCoin += currency.GiveFruitCoin } } return func(t *db.MongoTool) error { // 增加果币与总充值金额 w, err := walletmod.Credit(t, walletmod.CreditPlan{ FruitCoin: &fruitCoin, Consumption: &payMoney, }, order.UID) if err != nil { return err } return txnmod.InsertTransactionLog(t, &txnmod.TransactionLog{ TransNo: order.ID, UID: order.UID, FruitCoin: fruitCoin, TranType: txnmod.OfficialRech.Key(), TranTypeInt: int64(txnmod.OfficialRech), ChannelType: order.RechargeType, Desc: "官方充值-到账" + strconv.FormatInt(fruitCoin, 10) + "果币", DiscDoc: order.DiscDoc, SysType: order.DevType, FruitCoinBalance: w.FruitCoin, }) }, nil } // BuyGameCoin 购买游戏币 func BuyGameCoin(ctx context.Context, order rchgordmod.RechargeOrder, payMoney int64) (func(*db.MongoTool) error, error) { var ( currency currencymod.Currency u *usermod.User err error wg sync.WaitGroup creditPlan = walletmod.CreditPlan{Consumption: &payMoney} txnLogs = make([]txnmod.TransactionLog, 0) productId = order.ProductID.Hex() isFirstPay bool ) wg.Add(2) common.Go(func() { defer wg.Done() // 查询货币信息 currency, err = currencymod.Get(order.ProductID) if err != nil { log.ErrorX(ctx, fmt.Sprintf("充值游戏币-订单号[%s] 产品ID[%v] 查询异常[%v]", order.ID, order.ProductID, err)) } }) common.Go(func() { defer wg.Done() // 查询用户信息 u, err = usermod.FindUserByUID(order.UID) if err != nil { log.ErrorX(ctx, fmt.Sprintf("充值游戏币-订单号[%s] 用户ID[%d] 查询用户信息异常 [%v]", order.ID, order.UID, err)) } }) wg.Wait() if err != nil { return nil, err } if !u.HasFirstChargeVID { item, err := rchgordmod.FindOne(nil, bson.M{"uid": u.UID, "productType": 1, "status": rchgordmod.SUCCESS}) if err != nil { log.ErrorX(ctx, fmt.Sprintf("货币列表==>棋牌首次充值查询异常[%v]", err)) } if item == nil { isFirstPay = true } else { payTrue := true _, err = usermod.UpdateTrans(nil, u.UID, usermod.UserSelector{HasFirstChargeWaLi: &payTrue}) if err != nil { return nil, err } } } // 未匹配到对应配置 if currency.ID.IsZero() { log.ErrorX(ctx, fmt.Sprintf("充值游戏币-订单号[%s] 产品ID[%v] 未匹配到对应配置", order.ID, order.ProductID)) } else { // 匹配到对应配置 // 金额不能低于配置金额1元并且有赠送金额 if (payMoney + 100 - currency.Price) > 0 { // 赠送金币 if currency.GiveGold > 0 { creditPlan.Amount = ¤cy.GiveGold } // 赠送果币 if currency.GiveFruitCoin > 0 { creditPlan.FruitCoin = ¤cy.GiveFruitCoin } } } if u == nil { log.ErrorX(ctx, fmt.Sprintf("充值游戏币-订单号[%s] 用户ID[%d]不存在", order.ID, order.UID)) return nil, fmt.Errorf("充值游戏币-订单号[%s] 用户ID[%d]不存在", order.ID, order.UID) } return func(t *db.MongoTool) error { // 赠送会员天数 if currency.GiveVipDays > 0 || (currency.FirstGiveVipDays > 0 && isFirstPay) { level, expire := currency.HandlerGiveVip(u.VipLevel, u.VipExpireDate, isFirstPay) if err := usermod.VipInfoChange(t, order.UID, level, expire); err != nil { log.ErrorX(ctx, fmt.Sprintf("充值游戏币-订单号[%v] 产品ID[%v] 会员卡变更异常[%v]", order.ID, order.ProductID, err)) return err } } // 赠送楼凤解锁次数 if currency.LouFengUnlockTimes > 0 { if err := usermod.LouFengUnlockTimesIncr(t, order.UID, currency.LouFengUnlockTimes); err != nil { log.ErrorX(ctx, fmt.Sprintf("充值游戏币-订单号[%v] 产品ID[%v] 楼凤解锁次数变更异常[%v]", order.ID, order.ProductID, err)) return err } } // 钱包变更 w, err := walletmod.Credit(t, creditPlan, order.UID) if err != nil { log.ErrorX(ctx, fmt.Sprintf("充值游戏币-订单号[%v] 产品ID[%v] 钱包变更异常[%v]", order.ID, order.ProductID, err)) return err } // 赠送金币记录 if (payMoney+100-currency.Price) > 0 && currency.GiveGold > 0 { txnLogs = append(txnLogs, txnmod.TransactionLog{ TransNo: primitive.NewObjectID(), UID: u.UID, TranType: txnmod.CurrencyGive.Key(), TranTypeInt: int64(txnmod.CurrencyGive), Desc: fmt.Sprintf("官方充值赠送-到账%d金币", currency.GiveGold), DiscDoc: u.DiscDoc, SysType: u.SysType, Amount: currency.GiveGold, ActualAmount: float64(currency.GiveGold), ProductID: &productId, RealAmount: w.RealAmount(), }) } // 赠送果币记录 if (payMoney+100-currency.Price) > 0 && currency.GiveFruitCoin > 0 { txnLogs = append(txnLogs, txnmod.TransactionLog{ TransNo: primitive.NewObjectID(), UID: u.UID, TranType: txnmod.CurrencyGive.Key(), TranTypeInt: int64(txnmod.CurrencyGive), Desc: fmt.Sprintf("官方充值赠送-到账%d果币", currency.GiveFruitCoin), DiscDoc: u.DiscDoc, SysType: u.SysType, FruitCoin: currency.GiveFruitCoin, ProductID: &productId, FruitCoinBalance: w.FruitCoin, }) } if err = txnmod.InsertManyTransactionLog(t, txnLogs); err != nil { log.ErrorX(ctx, fmt.Sprintf("充值游戏币-订单号[%v] 产品ID[%v] 记录资金流水记录异常[%v]", order.ID, order.ProductID, err)) return err } if order.RechargeType == "usdt" { // 增加usdt可提现额度 if err = walletmod.AddUsdtAmount(t, order.UID, payMoney); err != nil { log.ErrorX(ctx, fmt.Sprintf("充值游戏币-订单号[%v] 产品ID[%v] 增加usdt可提现额度异常[%v]", order.ID, order.ProductID, err)) return err } } if isFirstPay { _, err = usermod.UpdateTrans(t, u.UID, usermod.UserSelector{HasFirstChargeWaLi: &isFirstPay}) if err != nil { return err } } return nil }, nil } // BuyProduct 购买商品 func BuyProduct(ctx context.Context, order rchgordmod.RechargeOrder, payMoney int64) (func(*db.MongoTool) error, int, time.Time, error) { var ( creditPlan = walletmod.CreditPlan{Consumption: &payMoney} u *usermod.User p *productmod.Product err error wg sync.WaitGroup csm int w *walletmod.Wallet vipExpireTime time.Time ) wg.Add(3) common.Go(func() { defer wg.Done() // 查询会员卡信息 p, err = productmod.FindProduct(order.ProductID, order.DevType) if err != nil { log.ErrorX(ctx, fmt.Sprintf("充值购买会员卡-订单号[%v] 产品ID[%v] 查询会员卡信息异常[%v]", order.ID, order.ProductID, err)) } // 检查用户VIP升级 productser.CheckUserUpgrade(order.UID, p) }) common.Go(func() { defer wg.Done() // 查询用户信息 u, err = usermod.FindUserByUID(order.UID) if err != nil { log.ErrorX(ctx, fmt.Sprintf("充值购买会员卡-订单号[%s] 用户ID[%d] 查询用户信息异常 [%v]", order.ID, order.UID, err)) } }) common.Go(func() { defer wg.Done() var errG error w, errG = walletmod.GetWallet(order.UID) if errG != nil { log.ErrorX(ctx, fmt.Sprintf("充值购买会员卡-订单号[%s] 用户ID[%d] 查询用户钱包异常 [%v]", order.ID, order.UID, err)) } }) wg.Wait() if p == nil { log.ErrorX(ctx, fmt.Sprintf("充值购买会员卡-订单号[%v] 产品ID[%v]不存在", order.ID, order.ProductID)) return nil, 0, vipExpireTime, fmt.Errorf("充值购买会员卡-订单号[%v] 产品ID[%v]不存在", order.ID, order.ProductID) } // 正常分成 if p.ChanSplitMod == 1 { csm = rchgordmod.ChanShareModVIP } if u == nil { log.ErrorX(ctx, fmt.Sprintf("充值购买会员卡-订单号[%v] 用户ID[%d]不存在", order.ID, order.UID)) return nil, 0, vipExpireTime, fmt.Errorf("充值购买会员卡-订单号[%v] 用户ID[%d]不存在", order.ID, order.UID) } // 充值金额低于会员卡售价1元 转化为充值对应金额金币 // 会员抵扣券抵扣后,期望实付 = 原价 - 实际抵扣额;否则折价支付会被误判为"低于售价"而全额转金币,导致VIP不发放 expectedPrice := p.DiscountedPrice*10 - order.DeductAmount if expectedPrice < 0 { expectedPrice = 0 } var ( disPay = payMoney - expectedPrice fn func(t *db.MongoTool) error ) if (p.ProductType != commod.AdvanceCard) && disPay < -100 { var ( gold = payMoney / 10 ) creditPlan.Amount = &gold // 金币分成 csm = rchgordmod.ChanShareCoin return func(t *db.MongoTool) error { w, err := walletmod.Credit(t, creditPlan, order.UID) if err != nil { return err } //插入一条支出流水 if err = txnmod.InsertTransactionLog(t, &txnmod.TransactionLog{ TransNo: order.ID, UID: order.UID, Amount: gold, ActualAmount: float64(gold), TranType: txnmod.OfficialRech.Key(), TranTypeInt: int64(txnmod.OfficialRech), ChannelType: order.RechargeType, Desc: fmt.Sprintf("官方充值购买[%s]实际支付[%d]元;全额转化为[%d]金币", p.Name, payMoney/100, payMoney/10), DiscDoc: order.DiscDoc, SysType: order.DevType, RealAmount: w.RealAmount(), }); err != nil { log.ErrorX(ctx, fmt.Sprintf("rechargeSer CallbackAddCoins Trans fail error:%+v order:%+v", err, order)) return err } return nil }, csm, vipExpireTime, nil } // 购买商品处理 switch p.ProductType { case commod.MeetingCard: fn = productser.NewBuyMeetingCard(order, p, "官方充值-购买[%s]", w) case commod.OTHER, commod.PhysicalGoods: fn = productser.NewBuyOtherCard(order, p, "官方充值-购买[%s]", w) case commod.VIP, commod.NEWUSERCard: if fn, vipExpireTime, err = productser.NewBuyVIP(order, p, "官方充值-购买[%s]", w); err != nil { return nil, 0, vipExpireTime, err } case commod.AdvanceCard: if err = handleAdvanceOrderPre(order, p, u); err != nil { log.ErrorX(ctx, fmt.Sprintf("官方充值-订单号[%s] 预处理预售卡异常[%v]", order.ID, err)) } if fn, vipExpireTime, err = productser.BuyAdvanceVIP(order, p, "官方充值-购买[%s]", w, payMoney); err != nil { return nil, 0, vipExpireTime, err } case commod.GameAdvanceCard: if fn, vipExpireTime, err = productser.BuyGameAdvanceVIP(order, p, "官方充值-购买[%s]", w); err != nil { return nil, 0, vipExpireTime, err } case commod.VideoDiscount: if fn, err = productser.NewBuyVideoDiscountCard(order, p, "官方充值-购买[%s]", w); err != nil { return nil, 0, vipExpireTime, err } case commod.VideoFreeCard: if fn, err = productser.NewBuyVideoFreeCard(order, p, "官方充值-购买[%s]", w); err != nil { return nil, 0, vipExpireTime, err } case commod.CoinMonthCard: if fn, err = productser.NewBuyCoinMonthCard(order, p, "官方充值-购买[%s]", w); err != nil { return nil, 0, vipExpireTime, err } case commod.WhoringCard: if fn, err = productser.NewBuyWhoringCard(order, p, "官方充值-购买[%s]", w); err != nil { return nil, 0, vipExpireTime, err } default: return nil, 0, vipExpireTime, fmt.Errorf("充值购买会员卡-订单号[%s] 商品ID[%s] 未知商品", order.ID, order.ProductID) } // 返回事务函数 return func(t *db.MongoTool) error { // 充值金额大于会员卡售价1角 转化为充值对应金额金币 if disPay > 9 { var ( gold = disPay / 10 ) creditPlan.Amount = &gold w, err := walletmod.Credit(t, creditPlan, order.UID) if err != nil { return err } //插入一条支出流水 if err = txnmod.InsertTransactionLog(t, &txnmod.TransactionLog{ TransNo: order.ID, UID: order.UID, Amount: gold, ActualAmount: float64(gold), TranType: txnmod.OfficialRech.Key(), TranTypeInt: int64(txnmod.OfficialRech), ChannelType: order.RechargeType, Desc: fmt.Sprintf("官方充值购买[%s]实际支付[%d]元;超额部分转化为[%d]金币", p.Name, payMoney/100, disPay), DiscDoc: order.DiscDoc, SysType: order.DevType, RealAmount: w.RealAmount(), }); err != nil { log.ErrorX(ctx, fmt.Sprintf("rechargeSer CallbackAddCoins Trans fail error:%+v order:%+v", err, order)) return err } } if err = fn(t); err != nil { return err } if (p.ProductType == commod.VIP || p.ProductType == commod.NEWUSERCard || p.ProductType == commod.AdvanceCard) && p.Duration > 0 { common.Go(func() { fmt.Println("buyvip complete once task", u.UID) if err = taskser.CompleteOnceTask(t, order.UID, oncetaskmod.OnceTaskTypeUserBuyVip); err != nil { fmt.Println("buyvip complete once task err", u.UID, err.Error()) } }) return nil } return nil }, csm, vipExpireTime, nil } func handleAdvanceOrderPre(order rchgordmod.RechargeOrder, product *productmod.Product, u *usermod.User) error { data, err := advanceordermod.IsExist(bson.M{"productID": product.ID, "uid": u.UID}) if err != nil { return errors.New("advance order query IsExist err") } // 预付订单 var advanceOrder advanceordermod.AdvanceOrder // 如果没有预售单,直接写入一笔 if data == nil || data.ID.IsZero() { prepaidPrivilege := advanceordermod.AdvanceCardPrepaidPrivilege{} if product.PrepaidPrivilege != nil { prepaidPrivilege = advanceordermod.AdvanceCardPrepaidPrivilege{ CoinVideoLimitPerDay: product.PrepaidPrivilege.CoinVideoLimitPerDay, LuckyDrawLimitPerDay: product.PrepaidPrivilege.LuckyDrawLimitPerDay, AiUndressLimitPerDay: product.PrepaidPrivilege.AiUndressLimitPerDay, DownloadLimitPerDay: product.PrepaidPrivilege.DownloadLimitPerDay, } } // 创建预售订单 now := time.Now() advanceOrder.UID = u.UID advanceOrder.CreatedAt = now advanceOrder.Status = advanceordermod.AdvanceProcessing advanceOrder.AdvanceAmount = product.AdvanceAmount * 10 advanceOrder.BalanceAmount = product.BalanceAmount * 10 advanceOrder.StartTime = product.StartTime advanceOrder.EndTime = product.EndTime advanceOrder.ProductID = product.ID advanceOrder.TotalAmount = (product.AdvanceAmount + product.BalanceAmount) * 10 advanceOrder.AdvOid = order.ID.Hex() advanceOrder.PrepaidPrivilege = prepaidPrivilege advanceOrder.TodayUse = advanceordermod.DayUse{} _, err = advanceordermod.CreditOrder(nil, &advanceOrder) if err != nil { log.Error("handleAdvanceOrderPre 创建预售订单失败", log.Any("advanceOrder", advanceOrder), log.E(err)) return err } return nil } if data.Status != advanceordermod.AdvanceSUCCESS { return nil } data.Status = advanceordermod.BalanceProcessing err = advanceordermod.Update(nil, data.ID, bson.M{"status": advanceordermod.BalanceProcessing, "balOid": order.ID.Hex()}) if err != nil { return err } return nil }