798 lines
23 KiB
Go
798 lines
23 KiB
Go
package rechargeser
|
|
|
|
import (
|
|
"91porn-server/app/service/productser"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
|
|
"91porn-server/app/appg"
|
|
"91porn-server/app/proto"
|
|
"91porn-server/common"
|
|
"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/goldextramod"
|
|
"91porn-server/models/v/prdcthsomod"
|
|
"91porn-server/models/v/productmod"
|
|
"91porn-server/models/v/productposimod"
|
|
"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"
|
|
)
|
|
|
|
// New_CurrencyList 货币列表
|
|
func New_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 []string // 金额区间
|
|
removeRepeat = make(map[int64]struct{}, 0) // 金额去重
|
|
productType int // 产品类型 棋牌:1 站群:0
|
|
err error // 错误
|
|
isFirstPay bool // 棋牌首次充值
|
|
)
|
|
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, FenToYuan(c.Price))
|
|
}
|
|
|
|
req := rchgutil.GainPayTypeReq{Money: moneys, DevType: sysType, DistrictCode: userDistrictCode(uid)}
|
|
bc, err := req.GetPayType()
|
|
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
|
|
}
|
|
|
|
// userDistrictCode 获取用户渠道码,查询失败按空渠道码处理
|
|
func userDistrictCode(uid uint64) string {
|
|
if uid == 0 {
|
|
return ""
|
|
}
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return ""
|
|
}
|
|
return u.DistrictCode
|
|
}
|
|
|
|
// getChannelDisplayName 获取支付渠道显示名称
|
|
func getChannelDisplayName(pType string) string {
|
|
channelNames := map[string]string{
|
|
"wechat": "微信(当前通道拥挤)",
|
|
"alipay": "支付宝(官方强烈推荐)",
|
|
"union": "银联",
|
|
"daichong": "代充",
|
|
"usdt": "USDT(官方)",
|
|
"quickUnion": "快捷银联",
|
|
}
|
|
|
|
if name, exists := channelNames[pType]; exists {
|
|
return name
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func FenToYuan(price int64) string {
|
|
return strconv.FormatInt(price/100, 10)
|
|
}
|
|
|
|
func YuanToFen(money string) int64 {
|
|
// 去除前后空格
|
|
money = strings.TrimSpace(money)
|
|
if money == "" {
|
|
return 0
|
|
}
|
|
|
|
// 检查是否为负数
|
|
isNegative := false
|
|
if strings.HasPrefix(money, "-") {
|
|
isNegative = true
|
|
money = money[1:]
|
|
}
|
|
|
|
// 按小数点分割
|
|
parts := strings.Split(money, ".")
|
|
|
|
switch len(parts) {
|
|
case 1:
|
|
// 只有整数部分,如 "123", "456"
|
|
yuan, err := strconv.ParseInt(parts[0], 10, 64)
|
|
if err != nil {
|
|
log.Warn(fmt.Sprintf("invalid integer part: %v", err))
|
|
return 0
|
|
}
|
|
result := yuan * 100
|
|
if isNegative {
|
|
result = -result
|
|
}
|
|
return result
|
|
|
|
case 2:
|
|
// 有整数和小数部分,如 "123.45", "78.9"
|
|
yuan, err := strconv.ParseInt(parts[0], 10, 64)
|
|
if err != nil {
|
|
log.Warn(fmt.Sprintf("invalid integer part: %v", err))
|
|
return 0
|
|
}
|
|
|
|
// 处理小数部分
|
|
decimalPart := parts[1]
|
|
if len(decimalPart) > 2 {
|
|
// 如果小数部分超过2位,进行四舍五入或截断
|
|
// 这里选择截断,也可以根据需要改为四舍五入
|
|
decimalPart = decimalPart[:2]
|
|
} else if len(decimalPart) == 1 {
|
|
// 如果只有1位小数,补零
|
|
decimalPart += "0"
|
|
}
|
|
|
|
fen, err := strconv.ParseInt(decimalPart, 10, 64)
|
|
if err != nil {
|
|
log.Warn(fmt.Sprintf("invalid decimal part: %v", err))
|
|
return 0
|
|
}
|
|
|
|
result := yuan*100 + fen
|
|
if isNegative {
|
|
result = -result
|
|
}
|
|
return result
|
|
|
|
default:
|
|
log.Warn(fmt.Sprintf("invalid money format: %s", money))
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func GetPayChannelDetails_New(money int64, payTypes []rchgutil.AllPayType, proT int) []rchgamtmod.PayChannelRes {
|
|
// 后台系统配置的支付方式展示名称与顺序
|
|
cfg := getPayTypeDisplayConfig()
|
|
channels := make([]rchgamtmod.PayChannelRes, 0)
|
|
for _, payType := range payTypes {
|
|
// 金额匹配检查
|
|
if YuanToFen(payType.Money) != money {
|
|
continue
|
|
}
|
|
|
|
// 处理支持的支付类型
|
|
for _, t := range payType.Types {
|
|
// 展示名称优先取后台配置,未配置则回退默认硬编码名称
|
|
channelName := cfg.typeName(t.Type)
|
|
if channelName == "" {
|
|
continue
|
|
}
|
|
|
|
channels = append(channels, rchgamtmod.PayChannelRes{
|
|
Type: t.Type,
|
|
TypeName: channelName,
|
|
Channel: t.Type, // 根据实际情况调整
|
|
})
|
|
}
|
|
}
|
|
// 按后台配置的顺序排序,未配置顺序的支付方式排在最后
|
|
cfg.sortChannels(channels)
|
|
return channels
|
|
}
|
|
|
|
func New_ProductList(uid uint64, sysType string, newUser bool, proT int) (res []proto.ProductList, err error) {
|
|
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()
|
|
if discountsError != nil {
|
|
err = goldsError
|
|
return
|
|
}
|
|
})
|
|
common.Go(func() {
|
|
defer wg.Done()
|
|
flag := true
|
|
|
|
data, goldsError = productmod.FindProductsByProductType(&flag, newUser, sysType)
|
|
if goldsError != nil {
|
|
err = goldsError
|
|
return
|
|
}
|
|
// 根据用户当前卡进行升级
|
|
productser.CheckUserUpgradeProducts(uid, data)
|
|
})
|
|
common.Go(func() {
|
|
defer wg.Done()
|
|
positionM, ps, positionL, positionError = productposimod.FindNamesWithStatusTrue()
|
|
if positionError != nil {
|
|
err = positionError
|
|
return
|
|
}
|
|
})
|
|
wg.Wait()
|
|
|
|
moneys := GetUniqueMoneys(data)
|
|
|
|
// 缓存支付通道信息
|
|
req := rchgutil.GainPayTypeReq{Money: moneys, DevType: sysType, DistrictCode: userDistrictCode(uid)}
|
|
bc, err := req.GetPayTypeFromCache()
|
|
if err != nil {
|
|
log.Warn(fmt.Sprintf("用户ID:%d;rchgutil GetPayType:%v", uid, err))
|
|
}
|
|
// 构建结果
|
|
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 {
|
|
chans := GetPayChannelDetails_New(v.DiscountedPrice*10, 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
|
|
}
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func GetUniqueMoneys(data []productmod.Product) []string {
|
|
moneySet := make(map[string]bool)
|
|
|
|
for _, p := range data {
|
|
money := FenToYuan(p.DiscountedPrice * 10)
|
|
moneySet[money] = true
|
|
if p.AdvanceAmount > 0 {
|
|
advanceAmount := FenToYuan(p.AdvanceAmount * 10)
|
|
moneySet[advanceAmount] = true
|
|
}
|
|
if p.BalanceAmount > 0 {
|
|
balanceAmount := FenToYuan(p.BalanceAmount * 10)
|
|
moneySet[balanceAmount] = true
|
|
}
|
|
}
|
|
|
|
moneys := make([]string, 0, len(moneySet))
|
|
for money := range moneySet {
|
|
moneys = append(moneys, money)
|
|
}
|
|
|
|
return moneys
|
|
}
|
|
|
|
// RefundCallBack 退款回调处理逻辑
|
|
func RefundCallBack(ctx context.Context, oid string, tradeNo string) 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.REFUND {
|
|
log.Warn(fmt.Sprintf("oid:%v, tradeNo:%v, 退款回调重复回调", oid, tradeNo))
|
|
return nil
|
|
}
|
|
|
|
if r.Status != rchgordmod.SUCCESS {
|
|
log.Warn(fmt.Sprintf("oid:%v, tradeNo:%v, 退款回调订单状态异常", oid, tradeNo))
|
|
return nil
|
|
}
|
|
|
|
var (
|
|
fn func(*db.MongoTool) error
|
|
now = time.Now()
|
|
desc = rchgordmod.Status(rchgordmod.REFUND).Desc()
|
|
remark = "订单退款"
|
|
)
|
|
switch r.BuyType {
|
|
case commod.BuyGold:
|
|
if fn, err = RefundGold(ctx, r); err != nil {
|
|
log.ErrorX(ctx, "退款回调购买金币异常", log.Any("oid", oid), log.Any("tradeNo", tradeNo), log.E(err))
|
|
return err
|
|
}
|
|
|
|
case commod.BuyProduct:
|
|
if fn, err = RefundProduct(ctx, r); 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.RefundCallBackModify(ctx, t, r.ID, rchgordmod.EditSelector{
|
|
OID: &oid,
|
|
UpdatedAt: now,
|
|
RefundAt: &now,
|
|
Status: rchgordmod.REFUND,
|
|
StatusDesc: &desc,
|
|
Remark: &remark,
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
return fn(t)
|
|
})
|
|
return err
|
|
}
|
|
if err != nil {
|
|
log.Error(fmt.Sprintf("oid:%s,refund callBack err:%v", oid, err))
|
|
return errors.New("退款失败")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// RefundGold 退款金币 payMoney单位为分
|
|
func RefundGold(ctx context.Context, order rchgordmod.RechargeOrder) (func(*db.MongoTool) error, error) {
|
|
payMoney := order.PayMoney
|
|
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, 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)
|
|
}
|
|
|
|
gold = -gold
|
|
payMoney = -payMoney
|
|
w, err := walletmod.Credit(t, walletmod.CreditPlan{
|
|
Amount: &gold,
|
|
Consumption: &payMoney,
|
|
}, order.UID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
desc := "官方充值-退款" + strconv.FormatInt(gold, 10) + "金币"
|
|
if goldExtra != 0 {
|
|
desc = "官方充值-退款" + strconv.FormatInt(originGold, 10) + "金币, 消耗金币加购券退款" + strconv.FormatUint(goldExtra, 10) + "金币"
|
|
}
|
|
return txnmod.InsertTransactionLog(t, &txnmod.TransactionLog{
|
|
TransNo: order.ID,
|
|
UID: order.UID,
|
|
Amount: gold,
|
|
ActualAmount: float64(gold),
|
|
TranType: txnmod.OrderRefund.Key(),
|
|
TranTypeInt: int64(txnmod.OrderRefund),
|
|
ChannelType: order.RechargeType,
|
|
Desc: desc,
|
|
DiscDoc: order.DiscDoc,
|
|
SysType: order.DevType,
|
|
RealAmount: w.RealAmount(),
|
|
})
|
|
}, nil
|
|
}
|
|
|
|
// RefundProduct 退款购买商品
|
|
func RefundProduct(ctx context.Context, order rchgordmod.RechargeOrder) (func(*db.MongoTool) error, error) {
|
|
payMoney := order.PayMoney
|
|
var (
|
|
creditPlan = walletmod.CreditPlan{Consumption: &payMoney}
|
|
u *usermod.User
|
|
p *productmod.Product
|
|
err error
|
|
wg sync.WaitGroup
|
|
w *walletmod.Wallet
|
|
)
|
|
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))
|
|
}
|
|
})
|
|
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, fmt.Errorf("充值购买会员卡-订单号[%v] 产品ID[%v]不存在", order.ID, order.ProductID)
|
|
}
|
|
|
|
if u == nil {
|
|
log.ErrorX(ctx, fmt.Sprintf("充值购买会员卡-订单号[%v] 用户ID[%d]不存在", order.ID, order.UID))
|
|
return nil, fmt.Errorf("充值购买会员卡-订单号[%v] 用户ID[%d]不存在", order.ID, order.UID)
|
|
}
|
|
// 充值金额低于会员卡售价1元 转化为充值对应金额金币
|
|
var (
|
|
disPay = payMoney - p.DiscountedPrice*10
|
|
fn func(t *db.MongoTool) error
|
|
)
|
|
if disPay < -100 {
|
|
var (
|
|
gold = -(payMoney / 10)
|
|
)
|
|
creditPlan.Amount = &gold
|
|
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.OrderRefund.Key(),
|
|
TranTypeInt: int64(txnmod.OrderRefund),
|
|
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
|
|
}, nil
|
|
}
|
|
// 购买商品处理
|
|
switch p.ProductType {
|
|
case commod.VIP, commod.NEWUSERCard:
|
|
if fn, err = RefundBuyVIP(order, p, "退款官方充值-购买[%s]", w); err != nil {
|
|
return nil, err
|
|
}
|
|
default:
|
|
return nil, 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.OrderRefund.Key(),
|
|
TranTypeInt: int64(txnmod.OrderRefund),
|
|
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
|
|
}
|
|
return nil
|
|
}, nil
|
|
}
|
|
|
|
func RefundBuyVIP(order rchgordmod.RechargeOrder, p *productmod.Product, desc string, w *walletmod.Wallet) (func(*db.MongoTool) error, error) {
|
|
var (
|
|
//商品购买记录
|
|
history = prdcthsomod.ProductHistory{
|
|
ID: primitive.NewObjectID(),
|
|
UID: order.UID,
|
|
ProductID: p.ID,
|
|
Name: p.Name,
|
|
Amount: p.DiscountedPrice,
|
|
ProductType: prdcthsomod.VIP,
|
|
DiscDoc: order.DiscDoc,
|
|
SysType: order.DevType,
|
|
ProductSnapShot: p,
|
|
}
|
|
sel usermod.UserSelector
|
|
creditPlan = walletmod.CreditPlan{}
|
|
)
|
|
// 资金流水记录
|
|
txnLogs := []txnmod.TransactionLog{
|
|
{
|
|
UID: order.UID,
|
|
TranType: txnmod.OrderRefundVip.Key(),
|
|
TranTypeInt: int64(txnmod.OrderRefundVip),
|
|
TransNo: history.ID,
|
|
Desc: fmt.Sprintf(desc, p.Name),
|
|
DiscDoc: order.DiscDoc,
|
|
SysType: order.DevType,
|
|
CurrencyType: txnmod.CurrencyTypeCash,
|
|
Amount: -p.DiscountedPrice,
|
|
ActualAmount: float64(-p.DiscountedPrice),
|
|
RealAmount: w.RealAmount(),
|
|
},
|
|
}
|
|
// 查询用户信息
|
|
u, err := usermod.FindUserByUID(order.UID)
|
|
if err != nil {
|
|
log.Error(fmt.Sprintf("退款充值购买会员卡-订单号[%s] 查询用户信息异常 [%v]", order.ID, err))
|
|
return nil, err
|
|
}
|
|
// VIP变更
|
|
if p.Duration > 0 {
|
|
var expire time.Time
|
|
// VIP未过期
|
|
if u.VipExpireDate.After(time.Now()) {
|
|
expire = u.VipExpireDate.AddDate(0, 0, -p.Duration)
|
|
} else {
|
|
expire = time.Now().AddDate(0, 0, -p.Duration)
|
|
}
|
|
log.Info(fmt.Sprintf("到期时间:%v", expire))
|
|
sel.VipExpireDate = &expire
|
|
}
|
|
// 增加金币视频免费天数
|
|
if p.GoldVideoFreeDay > 0 {
|
|
var expire time.Time
|
|
if u.GoldVideoFreeExpire.IsZero() || u.GoldVideoFreeExpire.Before(time.Now()) {
|
|
expire = time.Now().AddDate(0, 0, -p.GoldVideoFreeDay)
|
|
} else {
|
|
expire = u.GoldVideoFreeExpire.AddDate(0, 0, -p.GoldVideoFreeDay)
|
|
}
|
|
sel.GoldVideoFreeExpire = &expire
|
|
}
|
|
if p.BroadcastDays > 0 {
|
|
expire := time.Time{}
|
|
if u.BroadcastExpire.IsZero() || u.BroadcastExpire.Before(time.Now()) {
|
|
expire = time.Now().AddDate(0, 0, -p.BroadcastDays)
|
|
} else {
|
|
expire = u.BroadcastExpire.AddDate(0, 0, -p.BroadcastDays)
|
|
}
|
|
sel.BroadcastExpire = &expire
|
|
}
|
|
if p.DramaDays > 0 {
|
|
expire := usermod.RenewDramaExpire(u.DramaExpire, time.Now(), -p.DramaDays)
|
|
sel.DramaExpire = &expire
|
|
}
|
|
// 回收赠送金币
|
|
if p.GiveCoin > 0 {
|
|
coin := -p.GiveCoin
|
|
creditPlan.Amount = &coin
|
|
}
|
|
|
|
// 回收赠送下载次数
|
|
if p.DownloadCount > 0 {
|
|
count := -p.DownloadCount
|
|
creditPlan.DownloadCount = &count
|
|
}
|
|
|
|
// 回收赠送AI免费脱衣次数
|
|
if p.AiUndressCount > 0 {
|
|
aiUndressFreeTimes := int64(-p.AiUndressCount)
|
|
creditPlan.AiUndressFreeTimes = &aiUndressFreeTimes
|
|
}
|
|
|
|
return func(t *db.MongoTool) error {
|
|
// 钱包变更
|
|
if p.GiveCoin > 0 || p.DownloadCount > 0 || p.AiUndressCount > 0 {
|
|
w, err := walletmod.Credit(t, creditPlan, order.UID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if p.DownloadCount > 0 {
|
|
txnLogs = append(txnLogs, txnmod.TransactionLog{
|
|
UID: order.UID,
|
|
DownloadCount: p.DownloadCount,
|
|
TranType: txnmod.AdminDebitDownloadCount.Key(),
|
|
TranTypeInt: int64(txnmod.AdminDebitDownloadCount),
|
|
TransNo: history.ID,
|
|
Desc: fmt.Sprintf(desc, p.Name) + fmt.Sprintf("-回收赠送[%d]次数", p.DownloadCount),
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
})
|
|
}
|
|
|
|
// 插入购买会员卡回收赠送金币流水
|
|
if p.GiveCoin > 0 {
|
|
giveLog := txnmod.TransactionLog{
|
|
UID: order.UID,
|
|
Amount: p.GiveCoin,
|
|
ActualAmount: float64(p.GiveCoin),
|
|
TranType: txnmod.RefundVipCardGive.Key(),
|
|
TranTypeInt: int64(txnmod.RefundVipCardGive),
|
|
TransNo: history.ID,
|
|
Desc: fmt.Sprintf("购买%s-回收赠送金币[%v个]", p.Name, p.GiveCoin),
|
|
SysType: u.SysType,
|
|
RealAmount: w.RealAmount(),
|
|
}
|
|
txnLogs = append(txnLogs, giveLog)
|
|
}
|
|
|
|
// 回收赠送免费次数
|
|
if p.AiUndressCount > 0 {
|
|
txnLogs = append(txnLogs, txnmod.TransactionLog{
|
|
UID: order.UID,
|
|
Amount: int64(p.AiUndressCount),
|
|
ActualAmount: float64(p.AiUndressCount),
|
|
TranType: txnmod.AdminDebitAiUndressFreeTimes.Key(),
|
|
TranTypeInt: int64(txnmod.AdminDebitAiUndressFreeTimes),
|
|
TransNo: history.ID,
|
|
Desc: fmt.Sprintf(desc, p.Name) + fmt.Sprintf("-回收赠送AI脱衣免费次数[%d次]", p.AiUndressCount),
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: w.RealAmount(),
|
|
})
|
|
}
|
|
}
|
|
// 用户信息变更
|
|
if p.Duration > 0 || p.GoldVideoFreeDay > 0 || p.BroadcastDays > 0 || p.DramaDays > 0 {
|
|
if _, err = usermod.UpdateTrans(t, u.UID, sel); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// 保存资金流水记录
|
|
if err = txnmod.InsertManyTransactionLog(t, txnLogs); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}, nil
|
|
}
|