Files
rootandClaude Opus 5 8679200f41 Initial commit
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 13:57:10 +08:00

218 lines
6.4 KiB
Go

package rechargeser
import (
"context"
"errors"
"fmt"
"strconv"
"time"
"91porn-server/common"
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/common/stderr"
"91porn-server/models/v/prdcthsomod"
"91porn-server/models/v/productmod"
"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"
"91porn-server/web/webg"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// RechargeCallBack 充值成功回调处理逻辑
func RechargeCallBack(ctx context.Context, oid string, payMoney int64, tradeNo string, code int, paymentAt time.Time) error {
r, err := rchgordmod.FindRechargeOrderByID(tradeNo)
if err != nil {
return err
}
if r.Status == rchgordmod.SUCCESS {
return nil
}
if r.OID != "" && r.OID != oid {
log.Error("WalletSer RechargeCallBack ObjectIDFromHex fail error: 1:")
return errors.New("1")
}
if r.Status != rchgordmod.Processing {
log.Error(fmt.Sprintf("WalletSer RechargeCallBack status fail data:%+v:", r.Status))
return errors.New("1")
}
g, err := rchgamtmod.GetGoldByID(r.ProductID)
if err != nil {
return err
}
if g == nil {
return errors.New("invalid productID")
}
id, err := primitive.ObjectIDFromHex(tradeNo)
if err != nil {
log.ErrorX(ctx, "ObjectIDFromHex fail", log.Any("tradeNo", tradeNo), log.E(err))
return err
}
now := time.Now()
set := rchgordmod.EditSelector{
PayMoney: &payMoney,
OID: &oid,
PaymentAt: &paymentAt,
}
if stderr.Code(code) != stderr.Success {
set.Status = rchgordmod.FAILURE
statusDesc := rchgordmod.Status(rchgordmod.FAILURE).Desc()
set.StatusDesc = &statusDesc
set.FailureAt = &now
return rchgordmod.CallBackModifyWithProcessing(ctx, nil, id, set)
}
if err = webg.VideoDB.Trans(func(t *db.MongoTool) error {
set.SuccessAt = &now
set.Status = rchgordmod.SUCCESS
statusDesc := rchgordmod.Status(rchgordmod.FAILURE).Desc()
set.StatusDesc = &statusDesc
if err = rchgordmod.CallBackModifyWithProcessing(ctx, t, id, set); err != nil {
return err
}
//默认以实际支付金额上分
amount := payMoney / 10
//实际支付小于订单金额1块,返回错误
if amount < (g.Coins - 10) {
log.WarnX(ctx, "invalid;payMoney invalid payMoney value;", log.Any("gold coins", g.Coins), log.Any("amount", amount))
return errors.New("invalid payMoney value;")
}
//实际支付金额小于订单金额,范围在1块以内,按订单金额上分
if amount < g.Coins {
amount = g.Coins
}
wallet, err := walletmod.CreditAmount(t, amount, r.UID)
if err != nil {
return err
}
if err = walletmod.CreditConsumption(t, payMoney, r.UID); err != nil {
return err
}
tt := txnmod.TransactionLog{
TransNo: r.ID,
UID: r.UID,
Amount: r.Amount,
TranType: txnmod.Rchg.Key(),
TranTypeInt: int64(txnmod.Rchg),
ChannelType: r.RechargeType,
Desc: "充值新增-" + strconv.FormatInt(amount, 10),
DiscDoc: r.DiscDoc,
SysType: r.DevType,
ActualAmount: float64(r.Amount),
RealAmount: walletmod.GetRealAmount(wallet),
}
//插入一条支出流水
return txnmod.InsertTransactionLog(t, &tt)
}); err != nil {
log.ErrorX(ctx, "WalletSer RechargeCallBack Trans fail", log.E(err))
return err
}
if !r.VipID.IsZero() {
common.Go(func() { _ = buyVIP(r.UID, r.VipID, r.DevType) })
}
return nil
}
func checkVipRenew(u *usermod.User, p *productmod.Product) (time.Time, int, int) {
now := time.Now()
var end time.Time
level := p.VipLevel
payVidDiscount := p.PayVidDiscount
d := time.Hour * 24 * time.Duration(p.Duration)
if u.VipExpireDate.After(now) { //renew
end = u.VipExpireDate.Add(d)
if u.VipLevel > p.VipLevel { //当前用户的vip等级比这次购买的大,使用用户的
level = u.VipLevel
}
if u.PayVidDiscount > 0 && u.PayVidDiscount < p.PayVidDiscount {
payVidDiscount = u.PayVidDiscount
}
} else {
end = now.Add(d)
}
return end, level, payVidDiscount
}
func debitPlan(w *walletmod.Wallet, amt int64) *walletmod.DebitPlan {
p := walletmod.DebitPlan{}
l1 := w.Amount - amt
if l1 >= 0 { //amount够了
p.Amount = amt
return &p
}
// l1 < 0
p.Amount = w.Amount //amount 扣完
l2 := w.Income + l1
if l2 >= 0 {
p.Income = -l1
return &p
}
return nil //余额不足
}
func getDiscInfo(user *usermod.User) txnmod.DiscDoc {
discDoc := txnmod.DiscDoc{}
discDoc.IsDirect = user.IsDirect
discDoc.DistrictCode = user.DistrictCode
discDoc.PromSeqe = user.PromSeqe
return discDoc
}
// BuyProduct 购买产品产生的行为
func buyVIP(uid uint64, productID primitive.ObjectID, sys string) stderr.Code {
p, err := productmod.FindProduct(productID, sys)
if err != nil || p == nil {
return stderr.ErrParamError
}
u, err := usermod.FindUserByUID(uid)
if err != nil || u == nil {
return stderr.ErrDbQueryError
}
w, err := walletmod.GetWallet(uid)
if err != nil || w == nil {
return stderr.ErrDbQueryError
}
plan := debitPlan(w, p.DiscountedPrice)
if plan == nil {
return stderr.InsufficientBalance
}
vipExpire, vipLevel, payVidDiscount := checkVipRenew(u, p)
sel := usermod.UserSelector{VipExpireDate: &vipExpire, VipLevel: &vipLevel, PayVidDiscount: &payVidDiscount}
if p.DramaDays > 0 {
expire := usermod.RenewDramaExpire(u.DramaExpire, time.Now(), p.DramaDays)
sel.DramaExpire = &expire
}
if err = webg.VideoDB.Trans(func(t *db.MongoTool) error {
var err error
var wallet *walletmod.Wallet
if wallet, err = walletmod.Debit(t, plan, uid); err != nil { //扣钱
return err
}
history := prdcthsomod.ProductHistory{ //插入商品购买记录
UID: uid,
ProductID: productID,
Name: p.Name,
Amount: plan.Amount,
Income: plan.Income,
ProductType: prdcthsomod.VIP,
DiscDoc: u.DiscDoc,
SysType: u.SysType,
ProductSnapShot: p,
}
if err = prdcthsomod.InsertProductHistory(t, &history); err != nil {
return err
}
if err = usermod.UpdateVIP(t, uid, u.VipExpireDate, sel); err != nil {
return err
}
txnLog := txnmod.TransactionLog{UID: uid, Amount: -p.DiscountedPrice, TranType: txnmod.PayVIP.Key(), TranTypeInt: int64(txnmod.PayVIP), TransNo: history.ID, Desc: "购买了" + p.Name, DiscDoc: getDiscInfo(u), SysType: u.SysType, ActualAmount: -float64(p.DiscountedPrice), RealAmount: walletmod.GetRealAmount(wallet)}
return txnmod.InsertTransactionLog(t, &txnLog)
}); err != nil {
return stderr.ErrDbTransError
}
return stderr.Success
}