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
+534
View File
@@ -0,0 +1,534 @@
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 = &currency.GiveGold
}
// 赠送果币
if currency.GiveFruitCoin > 0 {
creditPlan.FruitCoin = &currency.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
}