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
+984
View File
@@ -0,0 +1,984 @@
package rechargeser
import (
"91porn-server/app/service/activityclient"
"91porn-server/app/service/adser"
"91porn-server/middleware/ua"
"91porn-server/models/v/advanceordermod"
"context"
"encoding/json"
"errors"
"fmt"
"strconv"
"sync"
"time"
"go.mongodb.org/mongo-driver/bson"
"91porn-server/app/appg"
"91porn-server/app/proto"
"91porn-server/app/service"
"91porn-server/app/service/productser"
"91porn-server/common"
"91porn-server/common/constant"
"91porn-server/common/constant/redisconst"
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/common/rchgutil"
"91porn-server/common/stderr"
"91porn-server/models/commod"
"91porn-server/models/v/currencymod"
"91porn-server/models/v/goldcfgmod"
"91porn-server/models/v/productmod"
"91porn-server/models/v/productposimod"
"91porn-server/models/v/rchgamegoldmod"
"91porn-server/models/v/rchgamtmod"
"91porn-server/models/v/rchgordmod"
"91porn-server/models/v/txnmod"
"91porn-server/models/v/usermod"
"91porn-server/models/v/walletmod"
"github.com/shopspring/decimal"
"go.mongodb.org/mongo-driver/bson/primitive"
)
/*
Recharge 充值
返回 充值链接、充值模式、错误
*/
func Recharge(ctx context.Context, in *RechargeRequest, ua ua.UA, deduct *ActivityDeduct) (string, string, error) {
u, err := usermod.FindUserByUID(in.UID)
if err != nil {
return "", "", err
}
if u == nil {
return "", "", errors.New("invalid user")
}
if u.HasLocked {
return "", "", errors.New("user is banned")
}
if err = in.validateAttribution(u.UID); err != nil {
return "", "", err
}
// 创建订单
order, advanceOrder, err := in.CreateOrder(u, ua, deduct)
if err != nil {
log.ErrorX(ctx, fmt.Sprintf("用户ID[%d] 支付方式[%s] 购买类型[%d] 产品ID[%v] 产品子ID[%v] 创建订单异常[%v]",
in.UID, in.RechargeType, in.BuyType, in.ProductID, in.ProductChildID, err))
return "", "", err
}
if err = rchgordmod.InsertRechargeOrder(order); err != nil {
log.ErrorX(ctx, fmt.Sprintf("用户ID[%d] 支付方式[%s] 购买类型[%d] 产品ID[%v] 产品子ID[%v] 保存订单异常[%v]",
in.UID, in.RechargeType, in.BuyType, in.ProductID, in.ProductChildID, err))
return "", "", err
}
if advanceOrder == nil || advanceOrder.ID.IsZero() {
advanceOrder.AdvOid = order.ID.Hex()
_, err = advanceordermod.CreditOrder(nil, advanceOrder)
if err != nil {
log.Error("RechargeRequest.CreateOrder 创建预售订单失败", log.Any("advanceOrder", advanceOrder), log.E(err))
return "", "", err
}
} else if advanceOrder.Status == advanceordermod.AdvanceProcessing {
// 修改订单号和预付款
err := advanceordermod.Update(nil, advanceOrder.ID, bson.M{"advOid": order.ID.Hex(), "advanceAmount": advanceOrder.AdvanceAmount, "totalAmount": advanceOrder.TotalAmount})
if err != nil {
log.Error("RechargeRequest.CreateOrder 修改预售订单失败", log.Any("advanceOrder.Status", advanceOrder.Status), log.E(err))
return "", "", err
}
} else if advanceOrder.Status == advanceordermod.AdvanceSUCCESS || advanceOrder.Status == advanceordermod.BalanceProcessing {
err := advanceordermod.Update(nil, advanceOrder.ID, bson.M{"status": advanceordermod.BalanceProcessing, "balOid": order.ID.Hex()})
if err != nil {
log.Error("RechargeRequest.CreateOrder 修改预售订单失败", log.Any("advanceOrder.Status", advanceOrder.Status), log.E(err))
return "", "", err
}
}
// 下单
now := time.Now()
res, err := (&rchgutil.Recharge{
TradeNo: order.ID.Hex(),
Money: rchgutil.FenToYuan(order.Money),
Type: order.RechargeType,
Info: rchgutil.PayInfo{
PlayerId: strconv.FormatUint(order.UID, 10),
PlayerIp: order.UserIP,
DeviceId: order.DevID,
Tel: order.Tel,
DeviceType: order.DevType,
},
}).ToPayNew(ctx)
if err != nil {
log.ErrorX(ctx, fmt.Sprintf("用户ID[%d] 支付方式[%s] 购买类型[%d] 产品ID[%v] 产品子ID[%v] 下单请求异常[%v]",
in.UID, in.RechargeType, in.BuyType, in.ProductID, in.ProductChildID, err))
return "", "", err
}
log.InfoX(ctx, fmt.Sprintf("用户ID[%d] 支付方式[%s] 购买类型[%d] 产品ID[%v] 产品子ID[%v] 下单返回信息[%v]",
in.UID, in.RechargeType, in.BuyType, in.ProductID, in.ProductChildID, res))
// 更新订单
set := rchgordmod.EditSelector{
Status: rchgordmod.Processing,
ProgressAt: &now,
OID: &res.OID,
Mode: &res.Mode,
//Channel: &res.CID,
Rate: &res.Rebate,
}
if err = rchgordmod.Update(nil, order.ID, set); err != nil {
log.ErrorX(ctx, fmt.Sprintf("用户ID[%d] 支付方式[%s] 购买类型[%d] 产品ID[%v] 产品子ID[%v] 更新订单异常[%v]",
in.UID, in.RechargeType, in.BuyType, in.ProductID, in.ProductChildID, err))
return "", "", err
}
log.InfoX(ctx, fmt.Sprintf("用户ID[%d] 支付方式[%s] 购买类型[%d] 产品ID[%v] 产品子ID[%v] 【下单成功】",
in.UID, in.RechargeType, in.BuyType, in.ProductID, in.ProductChildID))
// 会员抵扣券:建单成功异步回调活动服占用(active -> used),携实际抵扣额
if order.CouponID != "" {
activityclient.NotifyDeductCouponAsync(order.CouponID, order.ID.Hex(), activityclient.DeductCouponStatusUsed, order.DeductAmount)
}
return res.PayUrl, res.Mode, nil
}
func Recharge_new(ctx context.Context, rchg *rchgordmod.RechargeOrder) (string, string, error) {
u, err := usermod.FindUserByUID(rchg.UID)
if err != nil {
return "", "", err
}
if u == nil {
return "", "", errors.New("invalid user")
}
rchg.DevID = u.DevID
rchg.Name = u.Name
rchg.DiscDoc = u.DiscDoc
if rchg.ProductType == 0 {
if rchg.VipID.IsZero() {
// 金币充值
g, err := rchgamtmod.GetGoldByID(rchg.ProductID)
if err != nil {
return "", "", err
}
if g == nil {
err = errors.New("invalid productID")
return "", "", err
}
rchg.Amount = g.Coins
rchg.Money = g.Price
} else {
p, err := productmod.FindProduct(rchg.VipID, rchg.DevType)
if err != nil {
return "", "", err
}
if p == nil {
return "", "", errors.New("invalid productID")
}
//新用户8折
/*if u.CreatedAt.Add(time.Hour*24).After(time.Now()) && p.ProductType == prdcthsomod.VIP && (p.DiscountedPrice == 5000 || p.DiscountedPrice == 10000) {
p.DiscountedPrice = int64(float64(p.DiscountedPrice) * 0.8)
}*/
rchg.Amount = p.DiscountedPrice
rchg.Money = p.DiscountedPrice * 10
}
}
if rchg.ProductType == 1 && rchg.Money <= 0 {
return "", "", errors.New("invalid money")
}
now := time.Now()
set := rchgordmod.EditSelector{
Status: rchgordmod.Processing,
ProgressAt: &now,
ProductType: &rchg.ProductType,
}
statusDesc := rchgordmod.Status(set.Status).Desc()
set.StatusDesc = &statusDesc
channel := "self"
rchg.Channel = channel
if err = rchgordmod.InsertRechargeOrder(rchg); err != nil {
return "", "", err
}
var userLevel int
if rchg.ProductType == 1 && u.HasFirstChargeWaLi {
userLevel = 1
}
res, err := (&rchgutil.Rchg{
TransNo: rchg.ID.Hex(),
UID: strconv.FormatUint(u.UID, 10),
DevID: rchg.DevID,
UserIP: rchg.UserIP,
Name: rchg.Name,
Tel: u.Mobile,
DevType: rchg.DevType,
Money: rchg.Money,
Channel: channel,
CreatedAt: now,
PayMethod: rchg.RechargeType,
ProductType: rchg.ProductType,
UserLevel: userLevel,
}).ToPayNew(ctx)
if err != nil {
common.Go(func() {
if appg.Bot == nil {
return
}
//判断是否返回 渠道已经关闭 错误,此时商户将该渠道关闭
if err == rchgutil.PAY_CHANNEL_CLOSE_ERROR {
return
}
})
return "", "", err
}
set.OID = &res.OID
payUrl := res.PayUrl
mode := res.Mode
set.Mode = &mode
set.Channel = &res.CID
set.Rate = &(res.Rate)
return payUrl, mode, rchgordmod.Update(nil, rchg.ID, set)
}
// GetPayChannel根据充值金额选择支付渠道
func GetPayChannel_new(ctx context.Context, sysType string, proT int) (data []*rchgamtmod.GoldRes, err error) {
var golds []*currencymod.Currency
// var discounts []*goldcfgmod.DiscountConfig
var goldsError error
wg := sync.WaitGroup{}
wg.Add(1)
common.Go(func() {
defer wg.Done()
golds, goldsError = currencymod.GetCurrencyListByRedis(commod.Gold)
if goldsError != nil {
return
}
})
wg.Wait()
moneysM := make(map[int64]int)
for vsi := range golds {
moneysM[golds[vsi].Price] = 1
}
moneys := make([]int64, len(moneysM))
mi := 0
for k := range moneysM {
moneys[mi] = k
mi++
}
bc, _ := rchgutil.GetPayType(ctx, moneys, sysType, 0, 0)
data = make([]*rchgamtmod.GoldRes, 0, len(golds))
for _, v := range golds {
chans := GetPayChannelDetails_new(v.Price, bc, proT)
if len(chans) <= 0 { // 没有支付方式的充值金额不展示
continue
}
data = append(data, &rchgamtmod.GoldRes{
ID: v.ID,
RechargeType: chans,
Amount: v.Coins,
TypeName: v.Name,
Money: v.Price,
})
}
return
}
func isLegalCurrencyType(t commod.CurrencyType) bool {
return t == commod.Gold || t == commod.FruitCoin || t == commod.GameCoin
}
// CurrencyList 货币列表(新增果币)
func CurrencyList(ctx context.Context, uid uint64, sysType string, t commod.CurrencyType) ([]*proto.CurrencyListResponse, stderr.Code) {
if !isLegalCurrencyType(t) {
log.ErrorX(ctx, "货币类型异常", log.Any("CurrencyType", t))
return nil, stderr.ErrParamError
}
var (
wg sync.WaitGroup // 同步控制
currencys []currencymod.Currency // 货币列表
moneys []int64 // 金额区间
removeRepeat = make(map[int64]struct{}, 0) // 金额去重
productType int // 产品类型 棋牌:1 站群:0
err error // 错误
isFirstPay bool // 棋牌首次充值
userLevel = 0
)
wg.Add(1)
common.Go(func() {
defer wg.Done()
currencys, err = currencymod.List(t)
if err != nil {
log.ErrorX(ctx, "货币列表==>获取列表异常", log.Any("CurrencyType", t), log.E(err))
}
})
if t == commod.GameCoin {
wg.Add(1)
productType = 1
common.Go(func() {
defer wg.Done()
u, err := usermod.FindUserByUID(uid)
if err != nil {
log.Error(fmt.Sprintf("货币列表==>棋牌首次充值查询异常[%v]", err))
return
}
if u.HasFirstChargeWaLi {
userLevel = 1
} else {
isFirstPay = true
}
})
}
wg.Wait()
if err != nil {
return nil, stderr.ErrNetWorkBusy
}
for _, c := range currencys {
if _, ok := removeRepeat[c.Price]; ok {
continue
}
removeRepeat[c.Price] = struct{}{}
moneys = append(moneys, c.Price)
}
bc, err := rchgutil.GetPayType(ctx, moneys, sysType, productType, userLevel)
if err != nil {
log.ErrorX(ctx, "获取支付通道列表异常", log.Any("moneys", moneys), log.Any("sysType", sysType),
log.Any("productType", productType), log.E(err))
return nil, stderr.ErrNetWorkBusy
}
data := make([]*proto.CurrencyListResponse, 0, len(currencys))
for _, v := range currencys {
if v.Price < 100 {
continue
}
rechargeType := GetPayChannelDetails_new(v.Price, bc, productType)
if len(rechargeType) <= 0 { // 无支付方式的充值金额不需要展示
continue
}
c := proto.CurrencyListResponse{
ID: v.ID,
Amount: v.Coins,
TypeName: v.Name,
Money: v.Price,
GiveVipDays: v.GiveVipDays,
LouFengUnlockTimes: v.LouFengUnlockTimes,
GiveGold: v.GiveGold,
}
if isFirstPay || v.NotFirst || t == commod.FruitCoin || t == commod.Gold {
c.CouponDesc = v.CouponDesc
}
c.RechargeType = rechargeType
data = append(data, &c)
}
return data, stderr.Success
}
func getChanByte(pType string) string {
switch pType {
case "wechat": //微信
return "微信【当前通道拥挤】"
case "alipay": //支付宝
//return "支付宝【🔥限时返利15%金币】"
return "支付宝【官方强烈推荐】"
case "union": //支付宝
return "银联"
case "daichong": //代充
return "代充"
case "usdt":
return "usdt"
case "quickUnion":
return "快捷银联"
}
return ""
}
func GetPayChannelDetails_new(money int64, payTypes []rchgutil.RchgType, proT int) []rchgamtmod.PayChannelRes {
chans := make([]rchgamtmod.PayChannelRes, 0)
for k := range payTypes {
if payTypes[k].Money == money {
if payTypes[k].Alipay {
pName := getChanByte("alipay")
chans = append(chans, rchgamtmod.PayChannelRes{
Type: "alipay",
TypeName: pName,
Channel: "alipay",
})
}
if payTypes[k].Wechat {
pName := getChanByte("wechat")
chans = append(chans, rchgamtmod.PayChannelRes{
Type: "wechat",
TypeName: pName,
Channel: "wechat",
})
}
if payTypes[k].DaiChong {
pName := getChanByte("daichong")
channel := appg.Conf.DaiChong.H5domain
if proT == 1 {
channel = appg.Conf.DaiChong.H5domainQP
}
chans = append(chans, rchgamtmod.PayChannelRes{
Type: "daichong",
TypeName: pName,
Channel: channel,
})
}
if payTypes[k].USDT {
pName := getChanByte("usdt")
chans = append(chans, rchgamtmod.PayChannelRes{
Type: "usdt",
TypeName: pName,
Channel: "usdt",
})
}
if payTypes[k].QuickUnion {
pName := getChanByte("quickUnion")
chans = append(chans, rchgamtmod.PayChannelRes{
Type: "quickUnion",
TypeName: pName,
Channel: "quickUnion",
})
}
if payTypes[k].Union {
pName := getChanByte("union")
chans = append(chans, rchgamtmod.PayChannelRes{
Type: "union",
TypeName: pName,
Channel: "union",
})
}
break
}
}
return chans
}
func CallbackAddCoins(ctx context.Context, t *db.MongoTool, amount, decPay int64, desc string,
order rchgordmod.RechargeOrder) (err error) {
if err != nil {
return err
}
wallet, err1 := walletmod.Credit(t, walletmod.CreditPlan{Amount: &amount, Consumption: &decPay}, order.UID)
if err1 != nil {
return err1
}
tt := txnmod.TransactionLog{
TransNo: order.ID,
UID: order.UID,
Amount: amount,
ActualAmount: float64(amount),
TranType: txnmod.Rchg.Key(),
TranTypeInt: int64(txnmod.Rchg),
ChannelType: order.RechargeType,
Desc: desc,
DiscDoc: order.DiscDoc,
SysType: order.DevType,
RealAmount: walletmod.GetRealAmount(wallet),
}
//插入一条支出流水
if err = txnmod.InsertTransactionLog(t, &tt); err != nil {
log.ErrorX(ctx, fmt.Sprintf("rechargeSer CallbackAddCoins Trans fail error:%+v order:%+v", err, order))
return
}
return
}
// RechargeCallBack 充值成功回调处理逻辑
func RechargeCallBack(ctx context.Context, oid string, payMoney int64, tradeNo string, code int, paymentAt, successAt time.Time) error {
log.InfoX(ctx, "充值回调", log.Any("oid", oid), log.Any("tradeNo", tradeNo))
// 查询订单
r, err := rchgordmod.FindRechargeOrderByID(tradeNo)
if err != nil {
log.ErrorX(ctx, "充值回调查询订单异常", log.Any("oid", oid), log.Any("tradeNo", tradeNo), log.E(err))
return err
}
if r.ID.IsZero() {
log.WarnX(ctx, "充值回调订单不存在", log.Any("oid", oid), log.Any("tradeNo", tradeNo))
return errors.New("订单不存在")
}
if r.Status == rchgordmod.SUCCESS {
log.WarnX(ctx, "充值回调重复回调", log.Any("oid", oid), log.Any("tradeNo", tradeNo))
return nil
}
if r.Status == rchgordmod.REFUND {
log.WarnX(ctx, "充值回调重复回调,订单已退款", log.Any("oid", oid), log.Any("tradeNo", tradeNo))
return nil
}
if r.Status != rchgordmod.Processing {
log.WarnX(ctx, "充值回调订单状态异常", log.Any("oid", oid), log.Any("tradeNo", tradeNo), log.Any("status", r.Status))
return errors.New("订单状态异常")
}
if r.ProductType == 1 {
return errors.New("瓦力游戏已下架")
}
// 新版充值---根据购买类型处理
var (
fn func(*db.MongoTool) error
now = time.Now()
desc = rchgordmod.Status(rchgordmod.SUCCESS).Desc()
csm int // 分成模式
)
switch r.BuyType {
case commod.BuyGold:
csm = rchgordmod.ChanShareCoin
if fn, _, err = service.BuyGold(ctx, r, payMoney); err != nil {
log.ErrorX(ctx, "充值回调购买金币异常", log.Any("oid", oid), log.Any("tradeNo", tradeNo), log.E(err))
return err
}
case commod.BuyGameCoin:
if fn, err = service.BuyGameCoin(ctx, r, payMoney); err != nil {
log.ErrorX(ctx, "充值回调购买游戏币异常", log.Any("oid", oid), log.Any("tradeNo", tradeNo), log.E(err))
return err
}
case commod.BuyFruitCoin:
if fn, err = service.BuyFruitCoin(ctx, r, payMoney); err != nil {
log.ErrorX(ctx, "充值回调购买果币异常", log.Any("oid", oid), log.Any("tradeNo", tradeNo), log.E(err))
return err
}
case commod.BuyProduct:
if fn, csm, _, err = service.BuyProduct(ctx, r, payMoney); err != nil {
log.ErrorX(ctx, "充值回调购买商品异常", log.Any("oid", oid), log.Any("tradeNo", tradeNo), log.E(err))
return err
}
default: // 默认为旧版购买处理
log.InfoX(ctx, "充值回调旧版本处理", log.Any("oid", oid), log.Any("tradeNo", tradeNo))
return errors.New("旧版回调不再支持, 请使用新版回调方式")
}
// 新版处理
if r.BuyType > 0 {
err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
if err = rchgordmod.CallBackModifyWithProcessing(ctx, t, r.ID, rchgordmod.EditSelector{
PayMoney: &payMoney,
OID: &oid,
PaymentAt: &paymentAt,
SuccessAt: &successAt,
Status: rchgordmod.SUCCESS,
StatusDesc: &desc,
ChanShareMod: &csm,
}); err != nil {
return err
}
return fn(t)
})
if err == nil {
// ABC广告分组测试
common.Go(func() {
user, _ := usermod.FindUserByUID(r.UID)
if user != nil {
_ = adser.UpsertAdStat(context.Background(), user, time.Now(), 0, 0, payMoney)
}
})
// 会员抵扣券:支付成功异步回调活动服核销(used -> verified)
if r.CouponID != "" {
activityclient.NotifyDeductCouponAsync(r.CouponID, r.ID.Hex(), activityclient.DeductCouponStatusVerified, r.DeductAmount)
}
}
return err
}
id, err := primitive.ObjectIDFromHex(tradeNo)
if err != nil {
log.ErrorX(ctx, "ObjectIDFromHex fail", log.Any("tradeNo", tradeNo), log.E(err))
return err
}
set := rchgordmod.EditSelector{
PayMoney: &payMoney,
OID: &oid,
PaymentAt: &paymentAt,
}
if stderr.Code(code) != stderr.Success {
set.Status = rchgordmod.FAILURE
desc := rchgordmod.Status(rchgordmod.FAILURE).Desc()
set.StatusDesc = &desc
set.FailureAt = &now
set.SuccessAt = &successAt
return rchgordmod.CallBackModifyWithProcessing(ctx, nil, id, set)
}
if r.ProductID.IsZero() { //人民币购买会员卡
p, err := productmod.FindProduct(r.VipID, r.DevType)
if err != nil {
return err
}
if p == nil {
return errors.New("invalid vipID")
}
disMoney := payMoney - (p.DiscountedPrice * 10)
if disMoney < -constant.RechargeAmtTolerance {
if payMoney <= 0 {
return nil
}
tdesc := "购买「" + p.Name + "卡」实际支付" + strconv.FormatFloat(float64(payMoney)/100, 'f', 2, 64) +
",全额转为「" + strconv.FormatInt(payMoney/10, 10) + "」金币"
return appg.VideoDB.Trans(func(t *db.MongoTool) error {
csm := rchgordmod.ChanShareCoin
set.ChanShareMod = &csm
if err = rchgordmod.CallBackModifyWithProcessing(ctx, t, id, set); err != nil {
return err
}
return CallbackAddCoins(ctx, nil, payMoney/10, payMoney, tdesc, r)
})
}
//查询金币优惠
localAmount := p.DiscountedPrice
giveGold := false
var gold int64
disCount, err := goldcfgmod.GetGoldCgfByAmountAndType(ctx, localAmount, r.RechargeType)
if disCount != nil && err == nil {
giveGold = true
if disCount.IncrAmount != 0 {
localAmount = disCount.IncrAmount
gold = disCount.IncrAmount
} else if disCount.IncTax != 0.0 {
incr := decimal.NewFromFloat(disCount.IncTax)
local := decimal.NewFromInt(localAmount)
localAmount = localAmount + local.Mul(incr).IntPart()
}
}
if p.ChanSplitMod == 1 { //正常分成
csm := rchgordmod.ChanShareModVIP
set.ChanShareMod = &csm
}
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
set.SuccessAt = &successAt
set.Status = rchgordmod.SUCCESS
desc := rchgordmod.Status(rchgordmod.SUCCESS).Desc()
set.StatusDesc = &desc
if err = rchgordmod.CallBackModifyWithProcessing(ctx, t, id, set); err != nil {
return err
}
creditPlan := walletmod.CreditPlan{
Consumption: &payMoney,
}
if giveGold {
creditPlan.Amount = &localAmount
}
if _, err = walletmod.Credit(t, creditPlan, r.UID); err != nil {
log.ErrorX(ctx, "walletmod.Credit failed", log.Any("creditPlan", creditPlan), log.Any("uid", r.UID),
log.E(err))
return err
}
fPayMoney := float32(payMoney)
if disMoney > 9 {
tdesc := "购买「" + p.Name + "卡」实际支付" + strconv.FormatFloat(float64(fPayMoney)/100, 'f', 2, 64) +
",超额部分转为「" + strconv.FormatInt(disMoney/10, 10) + "」金币"
if err = CallbackAddCoins(ctx, t, disMoney/10, 0, tdesc, r); err != nil {
return err
}
}
if err = RechargeBuy_p(t, r.UID, *p, gold); err != nil {
log.ErrorX(ctx, "RechargeBuy_p failed", log.Any("uid", r.UID), log.Any("product", p),
log.Any("gold", gold), log.E(err))
return err
}
return nil
}); err != nil {
log.ErrorX(ctx, "WalletSer RechargeCallBack Trans fail", log.E(err))
return err
}
} else { //充值金币
amount := payMoney / 10
g, err := rchgamtmod.GetGoldByID(r.ProductID)
if err != nil {
return err
}
if g == nil {
return errors.New("invalid productID")
}
if !r.VipID.IsZero() {
if amount > (g.Coins-10) && amount < g.Coins {
amount = g.Coins
}
} else {
//充值赠送金币
localAmount := amount
giveGold := false
disCount, err := goldcfgmod.GetGoldCgfByAmountAndType(ctx, localAmount, r.RechargeType)
if disCount != nil && err == nil {
giveGold = true
if disCount.IncrAmount != 0 {
localAmount = disCount.IncrAmount
} else if disCount.IncTax != 0.0 {
incr := decimal.NewFromFloat(disCount.IncTax)
local := decimal.NewFromInt(localAmount)
localAmount = localAmount + local.Mul(incr).IntPart()
}
}
if giveGold {
amount += localAmount
}
}
csm := rchgordmod.ChanShareCoin
set.ChanShareMod = &csm
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
set.SuccessAt = &successAt
set.Status = rchgordmod.SUCCESS
desc := rchgordmod.Status(rchgordmod.SUCCESS).Desc()
set.StatusDesc = &desc
if err = rchgordmod.CallBackModifyWithProcessing(ctx, t, id, set); err != nil {
return err
}
if err = walletmod.CreditConsumption(t, payMoney, r.UID); err != nil {
return err
}
wallet, err := walletmod.CreditAmount(t, amount, r.UID)
if err != nil {
return err
}
tt := txnmod.TransactionLog{
TransNo: r.ID,
UID: r.UID,
Amount: amount,
ActualAmount: float64(amount),
TranType: txnmod.Rchg.Key(),
TranTypeInt: int64(txnmod.Rchg),
ChannelType: r.RechargeType,
Desc: "充值新增-" + strconv.FormatInt(amount, 10),
DiscDoc: r.DiscDoc,
SysType: r.DevType,
RealAmount: walletmod.GetRealAmount(wallet),
}
//插入一条支出流水
return txnmod.InsertTransactionLog(t, &tt)
}); err != nil {
log.ErrorX(ctx, "WalletSer RechargeCallBack Trans fail", log.E(err))
return err
}
}
// ABC广告分组测试
common.Go(func() {
user, _ := usermod.FindUserByUID(r.UID)
if user != nil {
_ = adser.UpsertAdStat(context.Background(), user, time.Now(), 0, 0, payMoney)
}
})
// //用户充值成功时做充值扣量
// executionRechargeDeduction(ctx, tradeNo)
return nil
}
func FindProductByTypeNew(ctx context.Context, uid uint64, sysType string, newUser bool, proT int) (res []proto.ProductList, err error) {
redisKey := redisconst.DataCachKey(productmod.RedisSetKey, "FindProductByTypeNew"+sysType+strconv.FormatBool(newUser))
if !appg.Redis.IsExist(redisKey) {
var data []productmod.Product
var discounts []*goldcfgmod.DiscountConfig
var positionM map[string]string
var positionL []string
var ps map[string]int64
var (
goldsError, discountsError, positionError error
)
wg := sync.WaitGroup{}
wg.Add(3)
//获取金币配置优惠
common.Go(func() {
defer wg.Done()
discounts, discountsError = goldcfgmod.GetGoldConfigList()
})
common.Go(func() {
defer wg.Done()
flag := true
data, goldsError = productmod.FindProductsByProductType(&flag, newUser, sysType)
if goldsError != nil {
err = goldsError
return
}
})
common.Go(func() {
defer wg.Done()
positionM, ps, positionL, positionError = productposimod.FindNamesWithStatusTrue()
if positionError != nil {
err = positionError
return
}
})
wg.Wait()
moneysM := make(map[int64]int)
for vsi := range data {
moneysM[data[vsi].DiscountedPrice*10] = 1
}
moneys := make([]int64, len(moneysM))
mi := 0
for k := range moneysM {
moneys[mi] = k
mi++
}
bc, _ := rchgutil.GetPayType(ctx, moneys, sysType, proT, 0)
res = make([]proto.ProductList, len(positionL))
for i := range positionL {
pl := make([]proto.VIPListRes, 0)
res[i] = proto.ProductList{
ShowType: ps[positionL[i]],
Position: positionM[positionL[i]],
List: pl,
PositionID: positionL[i],
}
}
for _, v := range data {
price := v.DiscountedPrice * 10
if v.ProductType == productmod.AdvanceCard {
price = v.AdvanceAmount * 10
}
chans := GetPayChannelDetails_new(price, bc, proT)
if v.ProductType == commod.PhysicalGoods {
v.Name = ""
}
temp := proto.VIPListRes{
Product: v,
}
if discountsError == nil && len(discounts) > 0 {
for i := 0; i < len(chans); i++ {
for _, d := range discounts {
if chans[i].Type == d.Type && v.DiscountedPrice == d.Amount {
chans[i].IncrAmount = d.IncrAmount
chans[i].IncTax = d.IncTax
}
}
}
}
temp.RechargeType = chans
for k := range res {
if positionM[v.Position] == res[k].Position {
res[k].List = append(res[k].List, temp)
continue
}
}
}
jsonBytes, err := json.Marshal(res)
if err != nil {
return res, err
}
_ = appg.Redis.Set(redisKey, string(jsonBytes), redisconst.DataCachExpire)
}
str, err := appg.Redis.Get(redisKey)
if err != nil || str == nil {
return
}
if err = json.Unmarshal([]byte(*str), &res); err != nil {
return
}
return
}
// RechargeBuy 充值回调时购买w
func RechargeBuy_p(t *db.MongoTool, uid uint64, p productmod.Product, giveGold int64) error {
switch p.ProductType {
case productmod.MeetingCard: //约会卡:
return productser.BuyMeetingCard_d(t, uid, p)
case productmod.OTHER, productmod.PHYSICALGOODS:
return productser.BuyOtherCard_d(t, uid, p)
case productmod.VIP, productmod.NewUser:
return productser.BuyVIP_d(t, uid, p, giveGold)
case productmod.VideoFreeCard:
return productser.BuyVideoFreeCard_d(t, uid, p)
case productmod.VideoDiscount:
return productser.BuyVideoDiscountCard_d(t, uid, p)
}
return errors.New("invalid productType")
}
func GetPayChannel_qp(ctx context.Context, uid uint64, sysType string) (data []*rchgamtmod.GameGoldRes, err error) {
gameGolds, err := rchgamegoldmod.GetGoldListByActiveTrue()
if err != nil {
return
}
moneys := make([]int64, len(gameGolds))
for i, v := range gameGolds {
moneys[i] = v.Price
}
if len(gameGolds) == 0 { //@todo:兼容旧版本,以后删除
moneys = []int64{5000, 10000, 28800, 39800, 59600, 100000, 300000, 500000} //单位分 金额50, ... ,5000元
for _, v := range moneys {
gameGolds = append(gameGolds, &rchgamegoldmod.Gold{
Coins: v,
Price: v,
})
}
}
firstPay := true
userLevel := 0
if u, _ := usermod.FindUserByUID(uid); u != nil {
if u.HasFirstChargeWaLi {
firstPay = false
userLevel = 1
}
}
bc, err := rchgutil.GetPayType(ctx, moneys, sysType, 1, userLevel)
if err != nil {
return
}
data = make([]*rchgamtmod.GameGoldRes, 0, len(gameGolds))
for _, v := range gameGolds {
chans := GetPayChannelDetails_new(v.Price, bc, 1)
if len(chans) <= 0 { // 没有支付方式的充值金额不展示
continue
}
gold := rchgamtmod.GameGoldRes{
Amount: v.Coins * 100,
TypeName: fmt.Sprintf("%d元充值", v.Price/100),
Money: v.Price,
}
if firstPay || v.NotFirst {
gold.CouponDesc = v.CouponDesc
}
gold.RechargeType = chans
data = append(data, &gold)
}
return
}
func GetUserTransactionDetails(uid, pageNum, pageSize uint64) (total int64, data []proto.TransactionResp, hasNext bool, err error) {
//获取用户成功的交易记录
total, rechangeOrders, hasNext, _ := rchgordmod.GetSuccessOrderUidList(uid, pageSize, pageNum)
rechangeOrdersLen := len(rechangeOrders)
goldProductIds := make([]primitive.ObjectID, 0, rechangeOrdersLen)
vipProductIds := make([]primitive.ObjectID, 0, rechangeOrdersLen)
productIdMap := make(map[primitive.ObjectID]primitive.ObjectID)
for _, v := range rechangeOrders {
if _, ok := productIdMap[v.ProductID]; !ok {
if v.BuyType == commod.BuyGold || v.BuyType == commod.BuyGameCoin || v.BuyType == commod.BuyFruitCoin {
goldProductIds = append(goldProductIds, v.ProductID)
} else {
vipProductIds = append(vipProductIds, v.ProductID)
}
productIdMap[v.ProductID] = v.ProductID
}
}
ctx := context.Background()
//根据产品id 查询对应的奖品
vipProductMap, err := productmod.ListByIDsMap(vipProductIds)
if err != nil {
log.ErrorX(ctx, "ListByIDsMap failed", log.Any("vipProductIds", vipProductIds), log.E(err))
return
}
//根据产品id 查询金币对应的一个奖品
goldProductMap, err := currencymod.ListByIdsMap(goldProductIds)
if err != nil {
log.ErrorX(ctx, "ListByIdsMap failed", log.Any("goldProductIds", goldProductIds), log.E(err))
return
}
data = make([]proto.TransactionResp, len(rechangeOrders))
for i, v := range rechangeOrders {
resp := proto.TransactionResp{
OrderId: v.OID,
Money: v.Money,
PaymentAt: v.PaymentAt,
CreatedAt: v.CreatedAt,
PayType: v.RechargeType,
Status: v.Status,
}
if resp.Status == 3 {
resp.Money = v.PayMoney
}
if v.BuyType == commod.BuyProduct {
if p := vipProductMap[v.ProductID]; p != nil {
resp.ProductName = p.Name
}
} else {
if p := goldProductMap[v.ProductID]; p != nil {
resp.ProductName = p.Name
}
}
data[i] = resp
}
return
}