3004 lines
90 KiB
Go
3004 lines
90 KiB
Go
package productser
|
|
|
|
import (
|
|
"91porn-server/common/constant/redisconst"
|
|
"91porn-server/common/game"
|
|
"91porn-server/middleware/ua"
|
|
"91porn-server/models/v/advanceordermod"
|
|
"91porn-server/models/v/dailytaskmod"
|
|
"91porn-server/models/v/discount_area_mod"
|
|
"91porn-server/models/v/imgroupmembermod"
|
|
"91porn-server/models/v/imgroupmod"
|
|
"91porn-server/models/v/media_buy_record_mod"
|
|
"91porn-server/models/v/mediamod"
|
|
"91porn-server/models/v/nakedchatmod"
|
|
"91porn-server/models/v/nakedchatordermod"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"time"
|
|
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
|
|
"91porn-server/app/appg"
|
|
"91porn-server/app/service/taskser"
|
|
"91porn-server/common"
|
|
"91porn-server/common/constant"
|
|
"91porn-server/common/db"
|
|
"91porn-server/common/log"
|
|
sli "91porn-server/common/slice"
|
|
"91porn-server/common/stderr"
|
|
"91porn-server/common/timeutil"
|
|
"91porn-server/models/commod"
|
|
"91porn-server/models/l/payvidlgmod"
|
|
"91porn-server/models/v/audiobookmod"
|
|
"91porn-server/models/v/backpackmod"
|
|
"91porn-server/models/v/newactivity"
|
|
"91porn-server/models/v/oncetaskmod"
|
|
"91porn-server/models/v/payaudiobookmod"
|
|
"91porn-server/models/v/prdcthsomod"
|
|
"91porn-server/models/v/prizemod"
|
|
"91porn-server/models/v/productmod"
|
|
"91porn-server/models/v/txnmod"
|
|
"91porn-server/models/v/usermod"
|
|
"91porn-server/models/v/videocoupon"
|
|
"91porn-server/models/v/videodiscountmod"
|
|
"91porn-server/models/v/vidmod"
|
|
"91porn-server/models/v/vipconfigmod"
|
|
"91porn-server/models/v/walletmod"
|
|
|
|
"github.com/shopspring/decimal"
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
)
|
|
|
|
// Buy 购买商品
|
|
func Buy(uid uint64, productType commod.ProductType, productID, couponID, serviceID primitive.ObjectID, num uint64, userContact, sys string,
|
|
ChapterID string, goldVideoCouponNum int, isH5 bool, ua ua.UA, ip string, experimentAttribution VIPExperimentAttribution) stderr.Code {
|
|
normalizedAttribution, err := validateVIPExperimentAttribution(uid, productID, experimentAttribution)
|
|
if err != nil {
|
|
log.Warn("invalid VIP experiment coin purchase attribution", log.Any("uid", uid), log.Any("productID", productID), log.E(err))
|
|
return stderr.ErrParamError
|
|
}
|
|
switch productType {
|
|
case prdcthsomod.VIP, commod.NEWUSERCard:
|
|
return BuyVIP(uid, productID, couponID, sys, false, ua, ip, normalizedAttribution)
|
|
case prdcthsomod.AdvanceCard:
|
|
return BuyAdvanceCard(uid, productID, couponID, sys, false, ua, ip, normalizedAttribution)
|
|
case prdcthsomod.GameAdvanceCard:
|
|
return BuyGameAdvanceCard(uid, productID, couponID, sys, false, ua, ip, normalizedAttribution)
|
|
case prdcthsomod.VIDEO:
|
|
return BuyVid(uid, productID, goldVideoCouponNum, ua, ip)
|
|
case prdcthsomod.Media:
|
|
return BuyMedia(uid, productID, ua, ip)
|
|
case prdcthsomod.MeetingCard:
|
|
return BuyMeetingCard(uid, productID, sys, ua, ip)
|
|
case prdcthsomod.OTHER, commod.PhysicalGoods:
|
|
return BuyOtherCard(uid, productID, sys, ua, ip)
|
|
case commod.AudioBook:
|
|
return BuyAudioBook(uid, productID, ChapterID, ua, ip)
|
|
case commod.VideoDiscount:
|
|
return BuyVideoDiscountCard(uid, productID, sys, ua, ip)
|
|
case commod.VideoFreeCard:
|
|
return BuyVideoFreeCard(uid, productID, sys, ua, ip)
|
|
case commod.ImGroup:
|
|
return BuyImGroup(uid, productID, sys)
|
|
case commod.NakedChat:
|
|
return BuyNakedChat(uid, productID, num, userContact, sys)
|
|
default:
|
|
return stderr.ErrParamError
|
|
}
|
|
}
|
|
|
|
func BuyNakedChat(uid uint64, pid primitive.ObjectID, num uint64, userContact, sys string) stderr.Code {
|
|
// 获取该裸聊
|
|
info, err := nakedchatmod.GetInfo(pid)
|
|
if err != nil {
|
|
return stderr.BuyFailed
|
|
}
|
|
if info.Price <= 0 {
|
|
return stderr.BuyFailed
|
|
}
|
|
var buyNum uint64
|
|
for _, v := range info.Options {
|
|
if num == v {
|
|
buyNum = v
|
|
break
|
|
}
|
|
}
|
|
// 并没有配置该项
|
|
if buyNum == 0 {
|
|
return stderr.BuyFailed
|
|
}
|
|
// 判断钱包余额是否足够, 做出扣款计划
|
|
wallet, err := walletmod.GetWallet(uid)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
if wallet == nil {
|
|
return stderr.InsufficientBalance
|
|
}
|
|
amount := int64(info.Price * buyNum)
|
|
// 扣除金币,加入群聊
|
|
plan := debitPlan(wallet, amount)
|
|
if plan == nil { // 余额不足无法做出扣款计划
|
|
return stderr.InsufficientBalance
|
|
}
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
wallet, err = walletmod.Debit(t, plan, uid)
|
|
if err != nil && err.Error() != "wallet not found" {
|
|
return err
|
|
}
|
|
orderId, err := nakedchatordermod.Insert(t, nakedchatordermod.NakedChatOrder{
|
|
Nid: info.ID,
|
|
Uid: uid,
|
|
UserContact: userContact,
|
|
Num: buyNum,
|
|
Price: info.Price,
|
|
Amount: amount,
|
|
Remark: fmt.Sprintf("购买裸聊%v分钟", buyNum),
|
|
Snapshot: info,
|
|
Status: 0,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
})
|
|
if err != nil {
|
|
return stderr.BuyFailed
|
|
}
|
|
var realAmount decimal.Decimal
|
|
if wallet != nil {
|
|
realAmount = walletmod.GetRealAmount(wallet)
|
|
}
|
|
tl := txnmod.TransactionLog{TransNo: orderId,
|
|
UID: uid,
|
|
Amount: -amount,
|
|
ActualAmount: float64(-amount),
|
|
TranType: txnmod.BuyNakedChat.Key(),
|
|
TranTypeInt: int64(txnmod.BuyNakedChat),
|
|
Desc: fmt.Sprintf("购买裸聊-%v-%v分钟", info.Title, buyNum),
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: realAmount,
|
|
}
|
|
if err = txnmod.InsertTransactionLog(t, &tl); err != nil {
|
|
return err
|
|
}
|
|
if err = nakedchatmod.IncSaleNum(t, info.ID, 1); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
log.Warn(fmt.Sprintf("productser BuyNakedChat Transaction err [%s]", err.Error()))
|
|
return stderr.BuyFailed
|
|
}
|
|
return stderr.Success
|
|
}
|
|
|
|
func BuyImGroup(uid uint64, pid primitive.ObjectID, sys string) stderr.Code {
|
|
// 获取该群组
|
|
group, err := imgroupmod.GetInfo(pid)
|
|
if err != nil {
|
|
return stderr.BuyFailed
|
|
}
|
|
// 判断是否已经加入
|
|
res, err := imgroupmembermod.GetInfoByCond(bson.M{"uid": uid, "groupId": group.GroupId})
|
|
if err != nil {
|
|
return stderr.BuyFailed
|
|
}
|
|
// 已经购买过了
|
|
if !res.ID.IsZero() && res.Status != 2 {
|
|
return stderr.BuyFailed
|
|
}
|
|
if group.Price == 0 {
|
|
// 直接写入记录
|
|
_, err = imgroupmembermod.Insert(nil, imgroupmembermod.ImGroupMember{
|
|
GroupId: group.GroupId,
|
|
Uid: uid,
|
|
Price: group.Price,
|
|
Status: 1,
|
|
CreatedAt: time.Now(),
|
|
})
|
|
if err != nil {
|
|
return stderr.BuyFailed
|
|
}
|
|
return stderr.Success
|
|
}
|
|
// 判断钱包余额是否足够, 做出扣款计划
|
|
wallet, err := walletmod.GetWallet(uid)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
if wallet == nil {
|
|
return stderr.InsufficientBalance
|
|
}
|
|
// 扣除金币,加入群聊
|
|
plan := debitPlan(wallet, group.Price)
|
|
if plan == nil { // 余额不足无法做出扣款计划
|
|
return stderr.InsufficientBalance
|
|
}
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
wallet, err = walletmod.Debit(t, plan, uid)
|
|
if err != nil && err.Error() != "wallet not found" {
|
|
return err
|
|
}
|
|
memberId, err := imgroupmembermod.Insert(t, imgroupmembermod.ImGroupMember{
|
|
GroupId: group.GroupId,
|
|
Uid: uid,
|
|
Price: group.Price,
|
|
Status: 1,
|
|
CreatedAt: time.Now(),
|
|
})
|
|
if err != nil {
|
|
return stderr.BuyFailed
|
|
}
|
|
var realAmount decimal.Decimal
|
|
if wallet != nil {
|
|
realAmount = walletmod.GetRealAmount(wallet)
|
|
}
|
|
tl := txnmod.TransactionLog{TransNo: memberId,
|
|
UID: uid,
|
|
Amount: -group.Price,
|
|
ActualAmount: float64(-group.Price),
|
|
TranType: txnmod.JoinGroup.Key(),
|
|
TranTypeInt: int64(txnmod.JoinGroup),
|
|
Desc: "加入群组付费-" + group.Name,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: realAmount,
|
|
}
|
|
if err = txnmod.InsertTransactionLog(t, &tl); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}); err != nil {
|
|
log.Warn(fmt.Sprintf("productser BuyImGroup Transaction err [%s]", err.Error()))
|
|
return stderr.BuyFailed
|
|
}
|
|
common.Go(func() {
|
|
// 群组成员加1
|
|
imgroupmod.IncMemberNum(nil, group.ID, 1)
|
|
})
|
|
return stderr.Success
|
|
}
|
|
|
|
func BuyMedia(uid uint64, mediaId primitive.ObjectID, ua ua.UA, ip string) stderr.Code {
|
|
hasBought, err := media_buy_record_mod.IsWholeBuy(uid, mediaId)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
if hasBought {
|
|
return stderr.RepeatPurchase
|
|
}
|
|
|
|
v, err := mediamod.GetInfo(mediaId)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
|
|
if v.ID.IsZero() {
|
|
return stderr.CodeEmptyData
|
|
}
|
|
if v.MediaType == mediamod.MediaTypeDrama {
|
|
// 短剧只能通过带 contentID 的单集购买入口解锁。
|
|
return stderr.ErrParamError
|
|
}
|
|
if v.Permission == 0 {
|
|
return stderr.Failure
|
|
}
|
|
if (v.Permission == 1 && v.Price == 0) || v.Permission == 2 {
|
|
return stderr.Success
|
|
}
|
|
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
|
|
code := buyMedia(*u, v, ua, ip) // 购买动漫类
|
|
if code == stderr.Success {
|
|
// 完成日常任务
|
|
taskser.CompleteDailyTask(nil, uid, dailytaskmod.DailyUnlockByGold)
|
|
}
|
|
return code
|
|
}
|
|
|
|
// buyMedia 整本购买acg
|
|
func buyMedia(u usermod.User, media mediamod.Media, ua ua.UA, ip string) stderr.Code {
|
|
/*
|
|
购买ACG时:
|
|
- VIP折扣和视频折扣卡不可用
|
|
- 视频抵用券不可用
|
|
- 金币视频抵用券不可用
|
|
*/
|
|
// 判断钱包余额是否足够, 做出扣款计划
|
|
wallet, err := walletmod.GetWallet(u.UID)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
if wallet == nil {
|
|
return stderr.InsufficientBalance
|
|
}
|
|
plan := debitPlan(wallet, media.Price)
|
|
if plan == nil { // 余额不足无法做出扣款计划
|
|
return stderr.InsufficientBalance
|
|
}
|
|
|
|
var orderId = primitive.NewObjectID()
|
|
var orderCreatedAt = time.Now()
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
wallet, err = walletmod.Debit(t, plan, u.UID)
|
|
if err != nil && err.Error() != "wallet not found" {
|
|
return err
|
|
}
|
|
|
|
pv := &media_buy_record_mod.MediaBuyRecord{
|
|
ID: orderId,
|
|
MediaId: media.ID,
|
|
MediaType: media.MediaType,
|
|
ContentId: primitive.NilObjectID,
|
|
Uid: u.UID,
|
|
Type: 1,
|
|
Coins: media.Price,
|
|
PayMoney: media.Price,
|
|
CreatedAt: orderCreatedAt,
|
|
UpdateTime: time.Now(),
|
|
}
|
|
|
|
if err = media_buy_record_mod.Create(t, pv); err != nil {
|
|
return err
|
|
}
|
|
var realAmount decimal.Decimal
|
|
if wallet != nil {
|
|
realAmount = walletmod.GetRealAmount(wallet)
|
|
}
|
|
tl := txnmod.TransactionLog{TransNo: pv.ID,
|
|
UID: u.UID,
|
|
Amount: -media.Price,
|
|
ActualAmount: float64(-media.Price),
|
|
TranType: txnmod.BuyAcg.Key(),
|
|
TranTypeInt: int64(txnmod.BuyAcg),
|
|
Desc: "ACG-整本购买-" + media.Title,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: realAmount,
|
|
}
|
|
if err = txnmod.InsertTransactionLog(t, &tl); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}); err != nil {
|
|
log.Warn(fmt.Sprintf("productser buyMedia Transaction err [%s]", err.Error()))
|
|
return stderr.BuyFailed
|
|
}
|
|
common.Go(func() {
|
|
if err = mediamod.IncPurchasesCount(media.ID, 1); err != nil {
|
|
log.Warn("media IncPurchaseCount err", log.Any("mediaId", media.ID), log.E(err))
|
|
}
|
|
})
|
|
|
|
return stderr.Success
|
|
}
|
|
|
|
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 < payVidDiscount && u.PayVidDiscount > 0 {
|
|
payVidDiscount = u.PayVidDiscount
|
|
}
|
|
} else {
|
|
end = now.Add(time.Duration(d))
|
|
}
|
|
return end, level, payVidDiscount
|
|
}
|
|
|
|
// 扣除计划, 返回如何扣除本次支出
|
|
func debitPlan(w *walletmod.Wallet, amt int64) *walletmod.DebitPlan {
|
|
l1 := w.Amount - amt // l1: 计算amout扣除以后的剩余值(负数的话则表示不足的值)
|
|
if l1 >= 0 { // amount够了 只需要在amount内扣除即可
|
|
return &walletmod.DebitPlan{
|
|
Amount: amt,
|
|
}
|
|
}
|
|
// amount不足时, 先扣除amout, 不足部分在income内扣除
|
|
if w.Income+l1 >= 0 { // income足够支付差额
|
|
return &walletmod.DebitPlan{
|
|
Amount: w.Amount,
|
|
Income: -l1,
|
|
}
|
|
}
|
|
return nil //余额不足
|
|
}
|
|
|
|
// BuyVIP 购买产品产生的行为
|
|
func BuyVIP(uid uint64, productID, couponID primitive.ObjectID, sys string, isRecharge bool, ua ua.UA, ip string, experimentAttribution VIPExperimentAttribution) 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.ErrNetWorkBusy
|
|
}
|
|
// 检查用户升级
|
|
CheckUserUpgrade(uid, p)
|
|
|
|
w, err := walletmod.GetWallet(uid)
|
|
if err != nil || w == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
|
|
var price int64 = p.DiscountedPrice
|
|
// 使用优惠卷(仅限金币购买)
|
|
if !couponID.IsZero() && p.IsAmountPay {
|
|
goodsDetail, err := backpackmod.GetGoodsDetail(couponID)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
if goodsDetail == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
price = decimal.NewFromInt(price).Mul(decimal.NewFromInt(goodsDetail.GoodsValue)).Div(decimal.NewFromInt(10)).IntPart()
|
|
}
|
|
plan := debitPlan(w, price)
|
|
if plan == nil {
|
|
return stderr.InsufficientBalance
|
|
}
|
|
if isRecharge {
|
|
plan.Amount -= p.GiveCoin
|
|
}
|
|
if p.DownloadCount > 0 {
|
|
plan.DownloadCount = -p.DownloadCount
|
|
}
|
|
vipExpire, vipLevel, payVidDiscount := checkVipRenew(u, p)
|
|
sel := usermod.UserSelector{VipExpireDate: &vipExpire, VipLevel: &vipLevel, PayVidDiscount: &payVidDiscount, VipName: &p.Name}
|
|
if p.GoldVideoFreeDay > 0 {
|
|
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.GoldVideoFreeLimit > u.GoldVideoFreeLimit {
|
|
sel.GoldVideoFreeLimit = &p.GoldVideoFreeLimit
|
|
}
|
|
}
|
|
if p.AiUndressCount > 0 {
|
|
plan.AiUndressFreeTimes = int64(-p.AiUndressCount)
|
|
}
|
|
if p.LuckyDrawCount > 0 {
|
|
plan.LotteryTimes = -p.LuckyDrawCount // 负数经 Debit 的 $inc 即为增加抽奖次数
|
|
}
|
|
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
|
|
}
|
|
plan.Consumption = 1
|
|
sel.ChatPrice = &p.ChatPrice
|
|
var orderId = primitive.NewObjectID()
|
|
var orderCreatedAt = time.Now()
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
var err error
|
|
discDoc := u.DiscDoc
|
|
wallet, err := walletmod.Debit(t, plan, uid)
|
|
if err != nil { //扣钱
|
|
return err
|
|
}
|
|
|
|
history := prdcthsomod.ProductHistory{ //插入商品购买记录
|
|
ID: orderId,
|
|
UID: uid,
|
|
ProductID: productID,
|
|
Name: p.Name,
|
|
Amount: plan.Amount,
|
|
Income: plan.Income,
|
|
ProductType: prdcthsomod.VIP,
|
|
SysType: u.SysType,
|
|
ProductSnapShot: p,
|
|
IsUpgrade: p.IsUpgrade,
|
|
CurrentVipName: p.CurrentVipName,
|
|
CurrentVipPrice: p.CurrentVipPrice,
|
|
PurchasePrice: p.PurchasePrice,
|
|
DiscDoc: discDoc,
|
|
CreatedAt: orderCreatedAt,
|
|
ExperimentID: experimentAttribution.ExperimentID,
|
|
ExperimentVariant: experimentAttribution.ExperimentVariant,
|
|
SessionID: experimentAttribution.SessionID,
|
|
}
|
|
if err = prdcthsomod.InsertProductHistory(t, &history); err != nil {
|
|
return err
|
|
}
|
|
if err = usermod.UpdateVIP(t, uid, u.VipExpireDate, sel); err != nil {
|
|
return err
|
|
}
|
|
txnLogs := []txnmod.TransactionLog{
|
|
{
|
|
UID: uid,
|
|
Amount: -price,
|
|
ActualAmount: float64(-price),
|
|
TranType: txnmod.PayVIP.Key(),
|
|
TranTypeInt: int64(txnmod.PayVIP),
|
|
TransNo: history.ID,
|
|
Desc: "VIP购买-" + p.Name,
|
|
DiscDoc: discDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
},
|
|
}
|
|
// 判断会员卡是否赠送AI脱衣免费次数
|
|
if p.AiUndressCount > 0 {
|
|
aiLog := txnmod.TransactionLog{
|
|
UID: uid,
|
|
Amount: int64(p.AiUndressCount),
|
|
ActualAmount: float64(p.AiUndressCount),
|
|
TranType: txnmod.VipCardGiveAiUndressFreeCount.Key(),
|
|
TranTypeInt: int64(txnmod.VipCardGiveAiUndressFreeCount),
|
|
TransNo: history.ID,
|
|
Desc: fmt.Sprintf("购买%s-赠送AI脱衣免费次数[%v次]", p.Name, p.AiUndressCount),
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
txnLogs = append(txnLogs, aiLog)
|
|
}
|
|
|
|
//插入购买会员卡赠送金币流水
|
|
if p.GiveCoin > 0 && isRecharge {
|
|
giveLog := txnmod.TransactionLog{
|
|
UID: uid,
|
|
Amount: p.GiveCoin,
|
|
ActualAmount: float64(p.GiveCoin),
|
|
TranType: txnmod.VipCardGive.Key(),
|
|
TranTypeInt: int64(txnmod.VipCardGive),
|
|
TransNo: history.ID,
|
|
Desc: "VIP购买-" + p.Name + "-赠送金币",
|
|
DiscDoc: discDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
txnLogs = append(txnLogs, giveLog)
|
|
if err = txnmod.InsertTransactionLog(t, &giveLog); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if p.DownloadCount > 0 {
|
|
txnLogs = append(txnLogs, txnmod.TransactionLog{
|
|
UID: uid,
|
|
DownloadCount: p.DownloadCount,
|
|
TranType: txnmod.GiveDownload.Key(),
|
|
TranTypeInt: int64(txnmod.GiveDownload),
|
|
TransNo: history.ID,
|
|
Desc: fmt.Sprintf("VIP购买-%v", p.Name) + fmt.Sprintf("-赠送[%d]次数", p.DownloadCount),
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
})
|
|
}
|
|
if p.LuckyDrawCount > 0 {
|
|
txnLogs = append(txnLogs, txnmod.TransactionLog{
|
|
UID: uid,
|
|
LotteryTimes: p.LuckyDrawCount,
|
|
TranType: txnmod.GiveLotteryTimesCount.Key(),
|
|
TranTypeInt: int64(txnmod.GiveLotteryTimesCount),
|
|
TransNo: history.ID,
|
|
Desc: fmt.Sprintf("VIP购买-%v", p.Name) + fmt.Sprintf("-赠送[%d]次抽奖", p.LuckyDrawCount),
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
})
|
|
}
|
|
|
|
if err = txnmod.InsertManyTransactionLog(t, txnLogs); err != nil {
|
|
log.Warn(fmt.Sprintf("productser BuyVIP Transaction err %s", err.Error()))
|
|
return err
|
|
}
|
|
if !couponID.IsZero() {
|
|
if err = backpackmod.UseGoods(t, couponID); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
// 金币视频观影券
|
|
if coupons := HandleGoldVideoCoupon(p, uid, videocoupon.GoldVideoCouponSourceVIP); len(coupons) > 0 {
|
|
if err = videocoupon.InsertManyTrans(t, coupons); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
common.Go(func() {
|
|
_ = taskser.CompleteOnceTask(nil, uid, oncetaskmod.OnceTaskTypeUserBuyVip)
|
|
})
|
|
return nil
|
|
}); err != nil {
|
|
log.Warn(fmt.Sprintf("productser BuyVIP Transaction err %s", err.Error()))
|
|
return stderr.BuyFailed //通知消息
|
|
}
|
|
|
|
return stderr.Success
|
|
}
|
|
|
|
// BuyAdvanceCard 购买产品产生的行为
|
|
func BuyAdvanceCard(uid uint64, productID, couponID primitive.ObjectID, sys string, isRecharge bool, ua ua.UA, ip string, experimentAttribution VIPExperimentAttribution) stderr.Code {
|
|
var (
|
|
sel usermod.UserSelector
|
|
payVidDiscount int
|
|
changeStatus int
|
|
price int64
|
|
dsc = "金币购买"
|
|
)
|
|
|
|
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.ErrNetWorkBusy
|
|
}
|
|
// 检查用户VIP升级
|
|
CheckUserUpgrade(uid, p)
|
|
|
|
data, err := advanceordermod.IsExist(bson.M{"uid": uid, "productID": productID})
|
|
if err != nil {
|
|
log.Error(fmt.Sprintf("金币购买预售卡-productID[%s] 查询预售订单信息异常 [%v]", productID.Hex(), err))
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
|
|
if data == nil || data.ID.IsZero() {
|
|
var advanceOrder advanceordermod.AdvanceOrder
|
|
prepaidPrivilege := advanceordermod.AdvanceCardPrepaidPrivilege{}
|
|
if p.PrepaidPrivilege != nil {
|
|
prepaidPrivilege = advanceordermod.AdvanceCardPrepaidPrivilege{
|
|
CoinVideoLimitPerDay: p.PrepaidPrivilege.CoinVideoLimitPerDay,
|
|
LuckyDrawLimitPerDay: p.PrepaidPrivilege.LuckyDrawLimitPerDay,
|
|
AiUndressLimitPerDay: p.PrepaidPrivilege.AiUndressLimitPerDay,
|
|
DownloadLimitPerDay: p.PrepaidPrivilege.DownloadLimitPerDay,
|
|
}
|
|
}
|
|
// 创建预售订单
|
|
now := time.Now()
|
|
advanceOrder.UID = u.UID
|
|
advanceOrder.CreatedAt = now
|
|
advanceOrder.Status = advanceordermod.AdvanceProcessing
|
|
advanceOrder.AdvanceAmount = p.AdvanceAmount * 10
|
|
advanceOrder.BalanceAmount = p.BalanceAmount * 10
|
|
advanceOrder.StartTime = p.StartTime
|
|
advanceOrder.EndTime = p.EndTime
|
|
advanceOrder.PrepaidPrivilege = prepaidPrivilege
|
|
advanceOrder.TodayUse = advanceordermod.DayUse{}
|
|
advanceOrder.ProductID = p.ID
|
|
advanceOrder.TotalAmount = (p.AdvanceAmount + p.BalanceAmount) * 10
|
|
data, err = advanceordermod.CreditOrder(nil, &advanceOrder)
|
|
if err != nil {
|
|
return stderr.ErrDbInsertError
|
|
}
|
|
changeStatus = advanceordermod.AdvanceSUCCESS
|
|
} else if data.Status == advanceordermod.AdvanceProcessing {
|
|
// 有预售订单,且是首付状态
|
|
changeStatus = advanceordermod.AdvanceSUCCESS
|
|
} else if (data.Status == advanceordermod.AdvanceSUCCESS && time.Now().After(p.StartTime)) || data.Status == advanceordermod.BalanceProcessing {
|
|
// 尾款预付状态
|
|
changeStatus = advanceordermod.BalanceSUCCESS
|
|
} else if data.Status == advanceordermod.AdvanceSUCCESS && !time.Now().After(p.StartTime) {
|
|
return stderr.AdvanceOrderPayTimeIsErr
|
|
} else if data.Status == advanceordermod.BalanceSUCCESS {
|
|
return stderr.AdvanceOrderStatusIsErr
|
|
}
|
|
|
|
w, err := walletmod.GetWallet(uid)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
if w == nil || w.ID.IsZero() {
|
|
return stderr.InsufficientBalance
|
|
}
|
|
|
|
var fullPrivilege bool
|
|
var duration = p.Duration
|
|
var vipLevel = p.VipLevel
|
|
var tranType = txnmod.BuyAdvanceVIP
|
|
if changeStatus == advanceordermod.AdvanceSUCCESS {
|
|
duration = p.AdvanceDuration
|
|
// 会员持续时间不能超过预付过期时间
|
|
if time.Now().AddDate(0, 0, duration).After(p.AdvanceExpires) {
|
|
td := p.AdvanceExpires.Sub(time.Now())
|
|
duration = int(math.Round(td.Hours() / 24))
|
|
if duration < 1 {
|
|
duration = 1
|
|
}
|
|
}
|
|
|
|
vipLevel = p.AdvanceVipLevel
|
|
price = p.AdvanceAmount
|
|
dsc = "预售预付金币购买"
|
|
}
|
|
if changeStatus == advanceordermod.BalanceSUCCESS {
|
|
price = p.BalanceAmount
|
|
dsc = "预售尾款金币购买"
|
|
fullPrivilege = true
|
|
tranType = txnmod.BuyBalanceVIP
|
|
}
|
|
|
|
// 使用优惠卷(仅限金币购买)
|
|
if !couponID.IsZero() && p.IsAmountPay {
|
|
goodsDetail, err := backpackmod.GetGoodsDetail(couponID)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
if goodsDetail == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
price = decimal.NewFromInt(price).Mul(decimal.NewFromInt(goodsDetail.GoodsValue)).Div(decimal.NewFromInt(10)).IntPart()
|
|
}
|
|
plan := debitPlan(w, price)
|
|
if plan == nil {
|
|
return stderr.InsufficientBalance
|
|
}
|
|
if isRecharge {
|
|
plan.Amount -= p.GiveCoin
|
|
}
|
|
|
|
// VIP变更
|
|
if duration > 0 {
|
|
var expire time.Time
|
|
payVidDiscount = p.PayVidDiscount
|
|
// VIP未过期
|
|
if u.VipExpireDate.After(time.Now()) {
|
|
if changeStatus == advanceordermod.BalanceSUCCESS {
|
|
expire = u.VipExpireDate.AddDate(0, 0, duration)
|
|
}
|
|
if changeStatus == advanceordermod.AdvanceSUCCESS {
|
|
expire = time.Now().AddDate(0, 0, duration)
|
|
}
|
|
|
|
if u.VipLevel > p.VipLevel {
|
|
vipLevel = u.VipLevel
|
|
}
|
|
if u.PayVidDiscount > 0 && u.PayVidDiscount < p.PayVidDiscount {
|
|
payVidDiscount = u.PayVidDiscount
|
|
}
|
|
} else {
|
|
expire = time.Now().AddDate(0, 0, duration)
|
|
}
|
|
log.Info(fmt.Sprintf("到期时间:%v", expire))
|
|
sel.VipExpireDate = &expire
|
|
sel.VipLevel = &vipLevel
|
|
sel.PayVidDiscount = &payVidDiscount
|
|
}
|
|
|
|
// 增加金币视频免费天数
|
|
if p.GoldVideoFreeDay > 0 && fullPrivilege {
|
|
var expire time.Time
|
|
if u.GoldVideoFreeExpire.IsZero() || u.GoldVideoFreeExpire.Before(time.Now()) {
|
|
if changeStatus == advanceordermod.BalanceSUCCESS {
|
|
expire = time.Now().AddDate(0, 0, p.GoldVideoFreeDay)
|
|
}
|
|
if changeStatus == advanceordermod.AdvanceSUCCESS {
|
|
expire = p.EndTime
|
|
}
|
|
} else {
|
|
if changeStatus == advanceordermod.BalanceSUCCESS {
|
|
expire = u.GoldVideoFreeExpire.AddDate(0, 0, p.GoldVideoFreeDay)
|
|
}
|
|
if changeStatus == advanceordermod.AdvanceSUCCESS {
|
|
expire = p.EndTime
|
|
}
|
|
}
|
|
sel.GoldVideoFreeExpire = &expire
|
|
sel.GoldVideoFreeLimit = &p.GoldVideoFreeLimit
|
|
}
|
|
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 && fullPrivilege {
|
|
expire := usermod.RenewDramaExpire(u.DramaExpire, time.Now(), p.DramaDays)
|
|
sel.DramaExpire = &expire
|
|
}
|
|
if p.AllGoldVideoFree && fullPrivilege {
|
|
free := true
|
|
sel.AllGoldVideoFree = &free
|
|
}
|
|
|
|
if p.Name != "" {
|
|
sel.VipName = &p.Name
|
|
}
|
|
|
|
if changeStatus == advanceordermod.BalanceSUCCESS && p.AiUndressCount > 0 {
|
|
plan.AiUndressFreeTimes = int64(-p.AiUndressCount)
|
|
}
|
|
if changeStatus == advanceordermod.BalanceSUCCESS && p.DownloadCount > 0 {
|
|
plan.DownloadCount = -p.DownloadCount
|
|
}
|
|
if changeStatus == advanceordermod.BalanceSUCCESS && p.LuckyDrawCount > 0 {
|
|
plan.LotteryTimes = -p.LuckyDrawCount
|
|
}
|
|
plan.Consumption = 1
|
|
sel.ChatPrice = &p.ChatPrice
|
|
var orderId = primitive.NewObjectID()
|
|
var orderCreatedAt = time.Now()
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
var err error
|
|
discDoc := u.DiscDoc
|
|
wallet, err := walletmod.Debit(t, plan, uid)
|
|
if err != nil { //扣钱
|
|
return err
|
|
}
|
|
|
|
history := prdcthsomod.ProductHistory{ //插入商品购买记录
|
|
ID: orderId,
|
|
UID: uid,
|
|
ProductID: productID,
|
|
Name: p.Name,
|
|
Amount: plan.Amount,
|
|
Income: plan.Income,
|
|
ProductType: prdcthsomod.AdvanceCard,
|
|
AdvanceOrderStatus: changeStatus, // 新增的预售订单状态
|
|
SysType: u.SysType,
|
|
ProductSnapShot: p,
|
|
IsUpgrade: p.IsUpgrade,
|
|
CurrentVipName: p.CurrentVipName,
|
|
CurrentVipPrice: p.CurrentVipPrice,
|
|
PurchasePrice: p.PurchasePrice,
|
|
DiscDoc: discDoc,
|
|
CreatedAt: orderCreatedAt,
|
|
ExperimentID: experimentAttribution.ExperimentID,
|
|
ExperimentVariant: experimentAttribution.ExperimentVariant,
|
|
SessionID: experimentAttribution.SessionID,
|
|
}
|
|
if err = prdcthsomod.InsertProductHistory(t, &history); err != nil {
|
|
return err
|
|
}
|
|
if err = usermod.UpdateVIP(t, uid, u.VipExpireDate, sel); err != nil {
|
|
return err
|
|
}
|
|
txnLogs := []txnmod.TransactionLog{
|
|
{
|
|
UID: uid,
|
|
Amount: -price,
|
|
ActualAmount: float64(-price),
|
|
TranType: tranType.Key(),
|
|
TranTypeInt: int64(tranType),
|
|
TransNo: history.ID,
|
|
Desc: fmt.Sprintf("%s-%s", dsc, p.Name),
|
|
DiscDoc: discDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
},
|
|
}
|
|
if changeStatus == advanceordermod.BalanceSUCCESS {
|
|
|
|
// 判断会员卡是否赠送AI脱衣免费次数
|
|
if p.AiUndressCount > 0 {
|
|
aiLog := txnmod.TransactionLog{
|
|
UID: uid,
|
|
Amount: int64(p.AiUndressCount),
|
|
ActualAmount: float64(p.AiUndressCount),
|
|
TranType: txnmod.VipCardGiveAiUndressFreeCount.Key(),
|
|
TranTypeInt: int64(txnmod.VipCardGiveAiUndressFreeCount),
|
|
TransNo: history.ID,
|
|
Desc: fmt.Sprintf("购买%s-赠送AI脱衣免费次数[%v次]", p.Name, p.AiUndressCount),
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
txnLogs = append(txnLogs, aiLog)
|
|
}
|
|
|
|
//插入购买会员卡赠送金币流水
|
|
if p.GiveCoin > 0 && isRecharge {
|
|
giveLog := txnmod.TransactionLog{
|
|
UID: uid,
|
|
Amount: p.GiveCoin,
|
|
ActualAmount: float64(p.GiveCoin),
|
|
TranType: txnmod.VipCardGive.Key(),
|
|
TranTypeInt: int64(txnmod.VipCardGive),
|
|
TransNo: history.ID,
|
|
Desc: "预售卡购买-" + p.Name + "-赠送金币",
|
|
DiscDoc: discDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
txnLogs = append(txnLogs, giveLog)
|
|
if err = txnmod.InsertTransactionLog(t, &giveLog); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if p.DownloadCount > 0 {
|
|
txnLogs = append(txnLogs, txnmod.TransactionLog{
|
|
UID: uid,
|
|
DownloadCount: p.DownloadCount,
|
|
TranType: txnmod.GiveDownload.Key(),
|
|
TranTypeInt: int64(txnmod.GiveDownload),
|
|
TransNo: history.ID,
|
|
Desc: fmt.Sprintf("预售卡购买-%v", p.Name) + fmt.Sprintf("-赠送[%d]次数", p.DownloadCount),
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
})
|
|
}
|
|
if p.LuckyDrawCount > 0 {
|
|
txnLogs = append(txnLogs, txnmod.TransactionLog{
|
|
UID: uid,
|
|
LotteryTimes: p.LuckyDrawCount,
|
|
TranType: txnmod.GiveLotteryTimesCount.Key(),
|
|
TranTypeInt: int64(txnmod.GiveLotteryTimesCount),
|
|
TransNo: history.ID,
|
|
Desc: fmt.Sprintf("预售卡购买-%v", p.Name) + fmt.Sprintf("-赠送[%d]次数", p.LuckyDrawCount),
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
})
|
|
}
|
|
}
|
|
// 修改预付订单状态
|
|
err = advanceordermod.Update(t, data.ID, bson.M{"status": changeStatus})
|
|
if err != nil {
|
|
log.Warn(fmt.Sprintf("productser Buy AdvanceCard advanceordermod Update Transaction err %s", err.Error()))
|
|
return err
|
|
}
|
|
if err = txnmod.InsertManyTransactionLog(t, txnLogs); err != nil {
|
|
log.Warn(fmt.Sprintf("productser Buy AdvanceCard Transaction err %s", err.Error()))
|
|
return err
|
|
}
|
|
if !couponID.IsZero() {
|
|
if err = backpackmod.UseGoods(t, couponID); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
// 金币视频观影券
|
|
if coupons := HandleGoldVideoCoupon(p, uid, videocoupon.GoldVideoCouponSourceVIP); len(coupons) > 0 {
|
|
if err = videocoupon.InsertManyTrans(t, coupons); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}); err != nil {
|
|
log.Warn(fmt.Sprintf("productser Buy AdvanceCard Transaction err %s", err.Error()))
|
|
return stderr.BuyFailed //通知消息
|
|
}
|
|
|
|
return stderr.Success
|
|
}
|
|
|
|
func BuyGameAdvanceCard(uid uint64, productID, couponID primitive.ObjectID, sys string, isRecharge bool, ua ua.UA, ip string, experimentAttribution VIPExperimentAttribution) stderr.Code {
|
|
var (
|
|
sel usermod.UserSelector
|
|
vipLevel int
|
|
payVidDiscount int
|
|
price int64
|
|
dsc = "金币购买"
|
|
)
|
|
|
|
p, err := productmod.FindProduct(productID, sys)
|
|
if err != nil || p == nil {
|
|
return stderr.ErrParamError
|
|
}
|
|
price = p.DiscountedPrice
|
|
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
|
|
w, err := walletmod.GetWallet(uid)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
if w == nil || w.ID.IsZero() {
|
|
return stderr.InsufficientBalance
|
|
}
|
|
|
|
plan := debitPlan(w, price)
|
|
if plan == nil {
|
|
return stderr.InsufficientBalance
|
|
}
|
|
if isRecharge {
|
|
plan.Amount -= p.GiveCoin
|
|
}
|
|
|
|
// 获取三方游戏码
|
|
gameCode, err := game.GainTripartiteGameCode(uid)
|
|
if err != nil {
|
|
log.Error(fmt.Sprintf("金币购买游戏预售-uid[%v] 获取游戏码信息异常 [%v]", uid, err))
|
|
return stderr.ErrReqForbidden
|
|
}
|
|
|
|
// VIP变更
|
|
if p.SendGame && p.Duration > 0 {
|
|
var expire time.Time
|
|
vipLevel = p.VipLevel
|
|
payVidDiscount = p.PayVidDiscount
|
|
// VIP未过期
|
|
if u.VipExpireDate.After(time.Now()) {
|
|
expire = u.VipExpireDate.AddDate(0, 0, p.Duration)
|
|
|
|
if u.VipLevel > p.VipLevel {
|
|
vipLevel = u.VipLevel
|
|
}
|
|
if u.PayVidDiscount > 0 && u.PayVidDiscount < p.PayVidDiscount {
|
|
payVidDiscount = u.PayVidDiscount
|
|
}
|
|
} else {
|
|
expire = time.Now().AddDate(0, 0, p.Duration)
|
|
}
|
|
log.Info(fmt.Sprintf("到期时间:%v", expire))
|
|
sel.VipExpireDate = &expire
|
|
sel.VipLevel = &vipLevel
|
|
sel.PayVidDiscount = &payVidDiscount
|
|
}
|
|
|
|
// 增加金币视频免费天数
|
|
if p.SendGame && 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.AllGoldVideoFree {
|
|
free := true
|
|
sel.AllGoldVideoFree = &free
|
|
}
|
|
|
|
if p.SendGame && p.Name != "" {
|
|
sel.VipName = &p.Name
|
|
}
|
|
|
|
if p.AiUndressCount > 0 {
|
|
plan.AiUndressFreeTimes = int64(-p.AiUndressCount)
|
|
}
|
|
if p.DownloadCount > 0 {
|
|
plan.DownloadCount = -p.DownloadCount
|
|
}
|
|
if p.LuckyDrawCount > 0 {
|
|
plan.LotteryTimes = -p.LuckyDrawCount
|
|
}
|
|
plan.Consumption = 1
|
|
if p.ChatPrice > 0 {
|
|
sel.ChatPrice = &p.ChatPrice
|
|
}
|
|
|
|
goodsList := make([]backpackmod.Backpack, 1)
|
|
now := time.Now()
|
|
goodsList[0] = backpackmod.Backpack{
|
|
UID: uid,
|
|
GoodsName: p.Name,
|
|
GoodsType: backpackmod.GameCode,
|
|
GoodsValue: p.DiscountedPrice,
|
|
GoodsOrigin: fmt.Sprintf("金币购买"),
|
|
GoodsDesc: gameCode,
|
|
Status: backpackmod.Unused,
|
|
ExpiredTime: now.AddDate(0, 0, int(p.Duration)),
|
|
CreateTime: now,
|
|
}
|
|
var orderId = primitive.NewObjectID()
|
|
var orderCreatedAt = time.Now()
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
var err error
|
|
discDoc := u.DiscDoc
|
|
wallet, err := walletmod.Debit(t, plan, uid)
|
|
if err != nil { //扣钱
|
|
return err
|
|
}
|
|
|
|
history := prdcthsomod.ProductHistory{ //插入商品购买记录
|
|
ID: orderId,
|
|
UID: uid,
|
|
ProductID: productID,
|
|
Name: p.Name,
|
|
Amount: plan.Amount,
|
|
Income: plan.Income,
|
|
ProductType: prdcthsomod.GameAdvanceCard,
|
|
DiscDoc: discDoc,
|
|
SysType: u.SysType,
|
|
GameCode: fmt.Sprintf("AAAA-B-%v", time.Now().Nanosecond()),
|
|
ProductSnapShot: p,
|
|
CreatedAt: orderCreatedAt,
|
|
ExperimentID: experimentAttribution.ExperimentID,
|
|
ExperimentVariant: experimentAttribution.ExperimentVariant,
|
|
SessionID: experimentAttribution.SessionID,
|
|
}
|
|
if err = prdcthsomod.InsertProductHistory(t, &history); err != nil {
|
|
return err
|
|
}
|
|
if err = usermod.UpdateVIP(t, uid, u.VipExpireDate, sel); err != nil {
|
|
return err
|
|
}
|
|
txnLogs := []txnmod.TransactionLog{
|
|
{
|
|
UID: uid,
|
|
Amount: -price,
|
|
ActualAmount: float64(-price),
|
|
TranType: txnmod.BuyGameAdvanceVIP.Key(),
|
|
TranTypeInt: int64(txnmod.BuyGameAdvanceVIP),
|
|
TransNo: history.ID,
|
|
Desc: fmt.Sprintf("%s-%s", dsc, p.Name),
|
|
DiscDoc: discDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
},
|
|
}
|
|
|
|
// 判断会员卡是否赠送AI脱衣免费次数
|
|
if p.AiUndressCount > 0 {
|
|
aiLog := txnmod.TransactionLog{
|
|
UID: uid,
|
|
Amount: int64(p.AiUndressCount),
|
|
ActualAmount: float64(p.AiUndressCount),
|
|
TranType: txnmod.VipCardGiveAiUndressFreeCount.Key(),
|
|
TranTypeInt: int64(txnmod.VipCardGiveAiUndressFreeCount),
|
|
TransNo: history.ID,
|
|
Desc: fmt.Sprintf("购买%s-赠送AI脱衣免费次数[%v次]", p.Name, p.AiUndressCount),
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
txnLogs = append(txnLogs, aiLog)
|
|
}
|
|
|
|
//插入购买会员卡赠送金币流水
|
|
if p.GiveCoin > 0 && isRecharge {
|
|
giveLog := txnmod.TransactionLog{
|
|
UID: uid,
|
|
Amount: p.GiveCoin,
|
|
ActualAmount: float64(p.GiveCoin),
|
|
TranType: txnmod.VipCardGive.Key(),
|
|
TranTypeInt: int64(txnmod.VipCardGive),
|
|
TransNo: history.ID,
|
|
Desc: "游戏预售卡购买-" + p.Name + "-赠送金币",
|
|
DiscDoc: discDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
txnLogs = append(txnLogs, giveLog)
|
|
if err = txnmod.InsertTransactionLog(t, &giveLog); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if p.DownloadCount > 0 {
|
|
txnLogs = append(txnLogs, txnmod.TransactionLog{
|
|
UID: uid,
|
|
DownloadCount: p.DownloadCount,
|
|
TranType: txnmod.GiveDownload.Key(),
|
|
TranTypeInt: int64(txnmod.GiveDownload),
|
|
TransNo: history.ID,
|
|
Desc: fmt.Sprintf("预售卡购买-%v", p.Name) + fmt.Sprintf("-赠送[%d]次数", p.DownloadCount),
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
})
|
|
}
|
|
|
|
// 保存用户游戏码
|
|
err = backpackmod.AddGoodsMany(t, uid, goodsList)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = txnmod.InsertManyTransactionLog(t, txnLogs); err != nil {
|
|
log.Warn(fmt.Sprintf("productser Buy AdvanceCard Transaction err %s", err.Error()))
|
|
return err
|
|
}
|
|
|
|
common.Go(func() {
|
|
_ = taskser.CompleteOnceTask(nil, uid, oncetaskmod.OnceTaskTypeUserBuyVip)
|
|
})
|
|
return nil
|
|
}); err != nil {
|
|
log.Warn(fmt.Sprintf("productser Buy AdvanceCard Transaction err %s", err.Error()))
|
|
return stderr.BuyFailed //通知消息
|
|
}
|
|
|
|
return stderr.Success
|
|
}
|
|
|
|
func debitPlanAmount(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
|
|
return nil
|
|
}
|
|
|
|
// VIPUp 购买产品产生的行为
|
|
func VIPUp(uid uint64) stderr.Code {
|
|
cfg, err := vipconfigmod.FindOne()
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
w, err := walletmod.GetWallet(uid)
|
|
if err != nil || w == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
vipExpire := time.Time{}
|
|
var vipUpPrice int64
|
|
vipLevel := 2
|
|
now := time.Now()
|
|
if !(u.VipExpireDate.After(now) && u.VipExpireDate.Before(now.AddDate(20, 0, 0))) {
|
|
return stderr.ErrInvalidRequest
|
|
}
|
|
vipExpire = u.VipExpireDate.AddDate(0, 0, 9999)
|
|
switch u.VipLevel {
|
|
case 1:
|
|
vipUpPrice = cfg.VipUpPrice
|
|
case 2:
|
|
vipUpPrice = cfg.SVipUpPrice
|
|
default:
|
|
}
|
|
plan := debitPlanAmount(w, vipUpPrice)
|
|
if plan == nil {
|
|
return stderr.InsufficientBalance
|
|
}
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
var err error
|
|
discDoc := u.DiscDoc
|
|
wallet, err := walletmod.Debit(t, plan, uid)
|
|
if err != nil { //扣钱
|
|
return err
|
|
}
|
|
if err = usermod.UpdateVIP(t, uid, u.VipExpireDate, usermod.UserSelector{VipExpireDate: &vipExpire, VipLevel: &vipLevel}); err != nil {
|
|
return err
|
|
}
|
|
txnLog := txnmod.TransactionLog{UID: uid,
|
|
Amount: -vipUpPrice,
|
|
ActualAmount: float64(-vipUpPrice),
|
|
TranType: txnmod.PayVIP.Key(),
|
|
TranTypeInt: int64(txnmod.PayVIP),
|
|
Desc: "升级永久VIP",
|
|
DiscDoc: discDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
return txnmod.InsertTransactionLog(t, &txnLog)
|
|
}); err != nil {
|
|
log.Warn(fmt.Sprintf("productser BuyVIP Transaction err %s", err.Error()))
|
|
return stderr.BuyFailed
|
|
}
|
|
return stderr.Success
|
|
}
|
|
|
|
func BuyVIP_d(t *db.MongoTool, uid uint64, p productmod.Product, giveGold int64) error {
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return errors.New("user is null")
|
|
}
|
|
//新用户8折
|
|
/*if u.CreatedAt.Add(time.Hour*24).After(time.Now()) && p.ProductType == prdcthsomod.VIP {
|
|
p.DiscountedPrice = int64(float64(p.DiscountedPrice) * 0.8)
|
|
}*/
|
|
vipExpire, vipLevel, payVidDiscount := checkVipRenew(u, &p)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
wallet, err := walletmod.GetWallet(uid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
history := prdcthsomod.ProductHistory{ //插入商品购买记录
|
|
ID: primitive.NewObjectID(),
|
|
UID: uid,
|
|
ProductID: p.ID,
|
|
Name: p.Name,
|
|
Amount: p.DiscountedPrice,
|
|
ProductType: prdcthsomod.VIP,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
ProductSnapShot: &p,
|
|
}
|
|
if err = prdcthsomod.InsertProductHistory(t, &history); err != nil {
|
|
return err
|
|
}
|
|
sel := usermod.UserSelector{VipExpireDate: &vipExpire, VipLevel: &vipLevel, PayVidDiscount: &payVidDiscount}
|
|
if p.GoldVideoFreeDay > 0 {
|
|
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.DramaDays > 0 {
|
|
expire := usermod.RenewDramaExpire(u.DramaExpire, time.Now(), p.DramaDays)
|
|
sel.DramaExpire = &expire
|
|
}
|
|
if err = usermod.UpdateVIP(t, uid, u.VipExpireDate, sel); err != nil {
|
|
return err
|
|
}
|
|
txnLogs := []txnmod.TransactionLog{txnmod.TransactionLog{UID: uid,
|
|
Amount: -p.DiscountedPrice,
|
|
ActualAmount: float64(-p.DiscountedPrice),
|
|
TranType: txnmod.PayVIP.Key(),
|
|
TranTypeInt: int64(txnmod.PayVIP),
|
|
TransNo: history.ID,
|
|
Desc: "VIP购买-" + p.Name,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
CurrencyType: txnmod.CurrencyTypeCash,
|
|
}}
|
|
// 插入日志
|
|
if giveGold > 0 {
|
|
giveLog := txnmod.TransactionLog{UID: uid,
|
|
Amount: giveGold,
|
|
ActualAmount: float64(giveGold),
|
|
TranType: txnmod.VipCardGive.Key(),
|
|
TranTypeInt: int64(txnmod.VipCardGive),
|
|
TransNo: history.ID,
|
|
Desc: "VIP购买-" + p.Name + "-赠送金币",
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
txnLogs = append(txnLogs, giveLog)
|
|
}
|
|
if err = txnmod.InsertManyTransactionLog(t, txnLogs); err != nil {
|
|
log.Warn(fmt.Sprintf("productser BuyVIP Transaction err %s", err.Error()))
|
|
return err
|
|
}
|
|
// 赠送观影券
|
|
coupons := HandleGoldVideoCoupon(&p, uid, videocoupon.GoldVideoCouponSourceVIP)
|
|
if len(coupons) > 0 {
|
|
if err = videocoupon.InsertMany(coupons); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func publiserTaxLevel(publisherID uint64) (int64, float64, int, int) {
|
|
u, err := usermod.FindUserByUID(publisherID)
|
|
if err != nil || u == nil {
|
|
return 0, 0, 0, 0
|
|
}
|
|
return u.TaxLevel, u.VideoDeduction, u.VideoDeductionCount, u.VideoDeductionPayCount
|
|
}
|
|
|
|
func BuyVid(uid uint64, vid primitive.ObjectID, goldVideoCouponNum int, ua ua.UA, ip string) stderr.Code {
|
|
var code stderr.Code
|
|
hasBought, err := payvidlgmod.IsPay4Video(uid, vid)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
if hasBought {
|
|
return stderr.RepeatPurchase
|
|
}
|
|
v, err := vidmod.GetVideoInfo(vid.Hex())
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
switch v.NewsType {
|
|
case vidmod.COVER, vidmod.PIC:
|
|
code = buyCoverVid(*u, v, ua, ip)
|
|
case vidmod.SEED_LINK:
|
|
code = buySeedUrlVid(*u, v, ua, ip)
|
|
default:
|
|
// 判断视频是否在折扣区
|
|
discountArea, err := discount_area_mod.GetDiscountAreaById(v.DiscountAreaId)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
// 用户是vip且视频在折扣区内打折,使用折扣区的购买逻辑
|
|
if u.IsVIP(time.Now()) && !discountArea.ID.IsZero() {
|
|
// 购买折扣区视频
|
|
code = buyDiscountAreaVideo(*u, v, discountArea, ua, ip)
|
|
} else {
|
|
// 购买普通视频帖子
|
|
code = buyNormalVid(*u, v, goldVideoCouponNum, ua, ip)
|
|
}
|
|
}
|
|
if code != stderr.Success {
|
|
return code
|
|
}
|
|
// 完成日常任务
|
|
taskser.CompleteDailyTask(nil, uid, dailytaskmod.DailyUnlockByGold)
|
|
|
|
return stderr.Success
|
|
}
|
|
|
|
// 购买种子链接帖子
|
|
func buySeedUrlVid(u usermod.User, v vidmod.VideoModel, ua ua.UA, ip string) stderr.Code { // 购买图片帖子
|
|
// 判断钱包余额是否足够, 做出扣款计划
|
|
wallet, err := walletmod.GetWallet(u.UID)
|
|
if err != nil || wallet == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
|
|
var orderId = primitive.NewObjectID()
|
|
var orderCreatedAt = time.Now()
|
|
|
|
plan := debitPlan(wallet, v.Coins)
|
|
if plan == nil { // 余额不足无法做出扣款计划
|
|
return stderr.InsufficientBalance
|
|
}
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
wallet, err = walletmod.Debit(t, plan, u.UID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
deducated := false
|
|
pIncome := decimal.NewFromInt(0)
|
|
publisherIncome, _ := pIncome.Float64()
|
|
taxAmount := float64(v.Coins)
|
|
pv := payvidlgmod.Pay4VidLog{
|
|
ID: orderId,
|
|
UID: u.UID,
|
|
VideoID: v.ID,
|
|
NewsType: v.NewsType,
|
|
PlayTime: v.PlayTime,
|
|
Coins: v.Coins,
|
|
PayMoney: v.Coins,
|
|
Tax: 0,
|
|
TaxAmount: taxAmount,
|
|
Title: v.Title,
|
|
PublisherIncome: publisherIncome,
|
|
PublisherID: v.PublisherID,
|
|
Uniq: payvidlgmod.Unique(u.UID, v.ID),
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
IsVideoDeduction: deducated,
|
|
CreatedAt: orderCreatedAt,
|
|
}
|
|
if err = payvidlgmod.InsertVideoPayRecord(t, pv); err != nil {
|
|
return err
|
|
}
|
|
tl := txnmod.TransactionLog{TransNo: pv.ID,
|
|
UID: u.UID,
|
|
Amount: -v.Coins,
|
|
ActualAmount: float64(-v.Coins),
|
|
TranType: txnmod.PayVID.Key(),
|
|
TranTypeInt: int64(txnmod.PayVID),
|
|
Desc: "付费种子链接-" + pv.Title,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
if err = txnmod.InsertTransactionLog(t, &tl); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}); err != nil {
|
|
log.Warn(fmt.Sprintf("productser BuyVid Transaction err [%s]", err.Error()))
|
|
return stderr.BuyFailed
|
|
}
|
|
if err = vidmod.IncPurchaseCount(v.ID); err != nil {
|
|
log.Warn("productser IncPurchaseCount err", log.Any("vid", v.ID), log.E(err))
|
|
}
|
|
|
|
return stderr.Success
|
|
}
|
|
|
|
// 购买图片帖子
|
|
func buyCoverVid(u usermod.User, v vidmod.VideoModel, ua ua.UA, ip string) stderr.Code { // 购买图片帖子
|
|
/*
|
|
购买图片时:
|
|
- VIP折扣和视频折扣卡不可用
|
|
- 视频抵用券不可用
|
|
- 金币视频抵用券不可用
|
|
*/
|
|
// 判断钱包余额是否足够, 做出扣款计划
|
|
wallet, err := walletmod.GetWallet(u.UID)
|
|
if err != nil || wallet == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
|
|
var orderId = primitive.NewObjectID()
|
|
var orderCreatedAt = time.Now()
|
|
|
|
plan := debitPlan(wallet, v.Coins)
|
|
if plan == nil { // 余额不足无法做出扣款计划
|
|
return stderr.InsufficientBalance
|
|
}
|
|
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
wallet, err = walletmod.Debit(t, plan, u.UID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
deducated := false
|
|
pIncome := decimal.NewFromInt(0)
|
|
publisherIncome, _ := pIncome.Float64()
|
|
taxAmount := float64(v.Coins)
|
|
pv := payvidlgmod.Pay4VidLog{
|
|
ID: orderId,
|
|
UID: u.UID,
|
|
VideoID: v.ID,
|
|
PlayTime: v.PlayTime,
|
|
Coins: v.Coins,
|
|
PayMoney: v.Coins,
|
|
NewsType: v.NewsType,
|
|
Tax: 0,
|
|
TaxAmount: taxAmount,
|
|
Title: v.Title,
|
|
PublisherIncome: publisherIncome,
|
|
PublisherID: v.PublisherID,
|
|
Uniq: payvidlgmod.Unique(u.UID, v.ID),
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
IsVideoDeduction: deducated,
|
|
CreatedAt: orderCreatedAt,
|
|
}
|
|
if err = payvidlgmod.InsertVideoPayRecord(t, pv); err != nil {
|
|
return err
|
|
}
|
|
tl := txnmod.TransactionLog{TransNo: pv.ID,
|
|
UID: u.UID,
|
|
Amount: -v.Coins,
|
|
ActualAmount: float64(-v.Coins),
|
|
TranType: txnmod.PayVID.Key(),
|
|
TranTypeInt: int64(txnmod.PayVID),
|
|
Desc: "付费图片-" + pv.Title,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
if err = txnmod.InsertTransactionLog(t, &tl); err != nil {
|
|
return err
|
|
}
|
|
// 图片不计入博主收益, 只计入博主图片出售数量
|
|
_ = usermod.IncCoverCount(v.PublisherID)
|
|
return nil
|
|
}); err != nil {
|
|
log.Warn(fmt.Sprintf("productser BuyVid Transaction err [%s]", err.Error()))
|
|
return stderr.BuyFailed
|
|
}
|
|
if err = vidmod.IncPurchaseCount(v.ID); err != nil {
|
|
log.Warn("productser IncPurchaseCount err", log.Any("vid", v.ID), log.E(err))
|
|
}
|
|
|
|
return stderr.Success
|
|
}
|
|
|
|
// 购买普通视频帖子
|
|
func buyNormalVid(u usermod.User, v vidmod.VideoModel, goldVideoCouponNum int, ua ua.UA, ip string) stderr.Code {
|
|
now := time.Now()
|
|
// 是否启用视频抵用券
|
|
videoCoupons, err := videocoupon.GetByUIDAndCouponNum(u.UID, goldVideoCouponNum)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
chooseCoupon, err := CheckGoldVideoCoupon(int64(goldVideoCouponNum), v.Coins, &u, videoCoupons)
|
|
if err != stderr.Success {
|
|
return err.(stderr.Code)
|
|
}
|
|
if u.IsVIP(now) && u.PayVidDiscount > 0 && goldVideoCouponNum <= 0 {
|
|
v.Coins = decimal.NewFromInt(int64(u.PayVidDiscount)).Shift(-1).
|
|
Mul(decimal.NewFromInt(v.Coins)).Round(0).IntPart()
|
|
}
|
|
goldVideoFreeLimit := usermod.GetGoldVideoFreeLimit(&u)
|
|
if !chooseCoupon {
|
|
// 计算用户是否拥有视频折扣卡
|
|
// 如果用户拥有视频折扣卡, 则购买视频时计算折后价
|
|
vDiscLog, err := videodiscountmod.GetByUID(u.UID)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
// 用户购买了视频免费卡(30金币以下免费)
|
|
if v.Coins < goldVideoFreeLimit && (u.GoldVideoFreeExpire.After(now) || (u.VideoFreeExpiration != nil && u.VideoFreeExpiration.After(now))) {
|
|
v.Coins = 0
|
|
// 直接返回成功,这个是个金币免费视频,不需要写入购买记录
|
|
return stderr.Success
|
|
} else {
|
|
// 用户购买了视频折扣卡
|
|
if vDiscLog.Expiration.After(now) && vDiscLog.VideoDiscount > 0 {
|
|
discountedCoins := decimal.NewFromInt(int64(vDiscLog.VideoDiscount)).Shift(-1).
|
|
Mul(decimal.NewFromInt(v.Coins)).Round(0).IntPart()
|
|
if discountedCoins < v.Coins {
|
|
v.Coins = discountedCoins // 若折扣后价格高于原价, 则按原价计算
|
|
}
|
|
}
|
|
}
|
|
if u.AllGoldVideoFree && u.GoldVideoFreeExpire.After(now) {
|
|
v.Coins = 0
|
|
}
|
|
}
|
|
|
|
// 判断钱包余额是否足够, 做出扣款计划
|
|
var wallet *walletmod.Wallet
|
|
wallet, err = walletmod.GetWallet(u.UID)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
|
|
var orderId = primitive.NewObjectID()
|
|
var orderCreatedAt = time.Now()
|
|
|
|
if (wallet == nil || wallet.ID.IsZero()) && v.Coins == 0 {
|
|
var p walletmod.CreditPlan
|
|
var amount int64 = 0
|
|
p.Amount = &amount
|
|
wallet, err = walletmod.Credit(nil, p, u.UID)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
if wallet == nil || wallet.ID.IsZero() {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
}
|
|
plan := debitPlan(wallet, v.Coins)
|
|
if goldVideoCouponNum <= 0 && plan == nil { // 当不使用抵用券且无法做出扣款计划时, 说明余额不足
|
|
return stderr.InsufficientBalance
|
|
}
|
|
taxLevel := constant.DefaultBloggerVideoIncomeTaxLevel
|
|
/** TODO:
|
|
默认视频分成为6/4分成
|
|
*/
|
|
tl, videoDeduction, videoDeductionCount, videoDeductionPayCount := publiserTaxLevel(v.PublisherID)
|
|
if tl > 0 && tl <= 10 {
|
|
taxLevel = tl
|
|
}
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
if !chooseCoupon { // 不使用金币视频抵用券, 根据扣款计划做出扣款
|
|
wallet, err = walletmod.Debit(t, plan, u.UID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
} else { // 使用金币折扣券, 则不扣款, 只进行抵用券扣除操作
|
|
var sel usermod.UserSelector
|
|
if u.GoldVideoCoupon != nil && len(u.GoldVideoCoupon) > 0 { // 兼容老数据
|
|
goldVideoCoupon := make([]usermod.UserGoldVideoCoupon, 0)
|
|
for _, gvc := range u.GoldVideoCoupon {
|
|
if gvc.Gold == goldVideoCouponNum {
|
|
gvc.Count -= 1
|
|
}
|
|
if gvc.Count > 0 {
|
|
goldVideoCoupon = append(goldVideoCoupon, gvc)
|
|
}
|
|
}
|
|
sel.GoldVideoCoupon = &goldVideoCoupon
|
|
//更新用户金币视频抵用券
|
|
if _, err = usermod.UpdateTrans(t, u.UID, sel); err != nil {
|
|
return err
|
|
}
|
|
usedCoupon := videocoupon.UserGoldVideoCoupon{
|
|
UID: u.UID,
|
|
Num: goldVideoCouponNum,
|
|
Source: videocoupon.GoldVideoCouponSourceVIP,
|
|
Used: true,
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
// 记录用户使用老版观影券
|
|
if err = videocoupon.InsertOne(usedCoupon); err != nil {
|
|
return err
|
|
}
|
|
} else { // 使用新版观影券
|
|
if err = videocoupon.UseOneCoupon(videoCoupons); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
// 是否扣量以及税率计算
|
|
deducated := checkVideoDeduction(videoDeduction, videoDeductionCount, videoDeductionPayCount, v.PublisherID, goldVideoCouponNum)
|
|
ta := decimal.NewFromInt(v.Coins).Mul(decimal.NewFromInt(taxLevel).Div(decimal.NewFromInt(10)))
|
|
pIncome := decimal.NewFromInt(v.Coins).Sub(ta)
|
|
publisherIncome, _ := pIncome.Float64()
|
|
taxAmount, _ := ta.Float64()
|
|
pv := payvidlgmod.Pay4VidLog{
|
|
ID: orderId,
|
|
UID: u.UID,
|
|
VideoID: v.ID,
|
|
PlayTime: v.PlayTime,
|
|
NewsType: v.NewsType,
|
|
Coins: v.Coins,
|
|
PayMoney: v.Coins,
|
|
Tax: taxLevel,
|
|
TaxAmount: taxAmount,
|
|
Title: v.Title,
|
|
PublisherIncome: publisherIncome,
|
|
PublisherID: v.PublisherID,
|
|
Uniq: payvidlgmod.Unique(u.UID, v.ID),
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
IsVideoDeduction: deducated,
|
|
CreatedAt: orderCreatedAt,
|
|
}
|
|
if err = payvidlgmod.InsertVideoPayRecord(t, pv); err != nil {
|
|
return err
|
|
}
|
|
tl := txnmod.TransactionLog{TransNo: pv.ID,
|
|
UID: u.UID,
|
|
Amount: -v.Coins,
|
|
ActualAmount: float64(-v.Coins),
|
|
TranType: txnmod.PayVID.Key(),
|
|
TranTypeInt: int64(txnmod.PayVID),
|
|
Desc: "付费视频-" + pv.Title,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
if goldVideoCouponNum > 0 {
|
|
tl.Desc = "金币抵用券" + strconv.Itoa(goldVideoCouponNum) + "-购买视频-" + pv.Title
|
|
tl.TranType = txnmod.GoldCouplePayVID.Key()
|
|
tl.TranTypeInt = int64(txnmod.GoldCouplePayVID)
|
|
tl.ActualAmount = 0
|
|
}
|
|
if err = txnmod.InsertTransactionLog(t, &tl); err != nil {
|
|
return err
|
|
}
|
|
// 扣量判断
|
|
if deducated {
|
|
return nil
|
|
}
|
|
//此时计算收益 收益分为两个部分 整数部分 10 小数部分 1
|
|
//计算小数部分
|
|
//与当前收益相加
|
|
pw, err := walletmod.GetWallet(v.PublisherID)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
pwPot, vidIncome := float64(0), float64(0)
|
|
if pw != nil {
|
|
pwPot = pw.IncomePot
|
|
vidIncome = pw.VidIncome
|
|
}
|
|
income := pIncome.Add(decimal.NewFromFloat(pwPot))
|
|
incomef, _ := pIncome.Add(decimal.NewFromFloat(vidIncome)).Float64()
|
|
incomeInt := income.IntPart()
|
|
incomePot, _ := income.Sub(decimal.NewFromInt(incomeInt)).Float64()
|
|
//增加钱包虚拟货币
|
|
wallet, err = walletmod.CreditIncomeBasePot(t, incomeInt, incomef, incomePot, v.PublisherID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tl = txnmod.TransactionLog{TransNo: pv.ID,
|
|
UID: v.PublisherID,
|
|
RechargeId: u.UID,
|
|
Amount: v.Coins,
|
|
ActualAmount: publisherIncome,
|
|
Tax: taxLevel,
|
|
TaxAmount: taxAmount,
|
|
TranType: txnmod.WorksIncome.Key(),
|
|
TranTypeInt: int64(txnmod.WorksIncome),
|
|
Desc: "视频收益-" + v.Title,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
|
|
//插入一收益流水
|
|
return txnmod.InsertTransactionLog(t, &tl)
|
|
}); err != nil {
|
|
log.Warn(fmt.Sprintf("productser BuyVid Transaction err [%s]", err.Error()))
|
|
return stderr.BuyFailed
|
|
}
|
|
if err = vidmod.IncPurchaseCount(v.ID); err != nil {
|
|
log.Warn("productser IncPurchaseCount err", log.Any("vid", v.ID), log.E(err))
|
|
}
|
|
|
|
return stderr.Success
|
|
}
|
|
|
|
// 购买折扣专区的视频
|
|
func buyDiscountAreaVideo(u usermod.User, v vidmod.VideoModel, discountArea discount_area_mod.DiscountArea, ua ua.UA, ip string) stderr.Code {
|
|
// 视频原价
|
|
originCoins := v.Coins
|
|
// 如果是在折扣专区里,按照折扣专区价格来
|
|
if !discountArea.ID.IsZero() {
|
|
newPrice := decimal.NewFromInt(int64(discountArea.Discount)).Shift(-2).
|
|
Mul(decimal.NewFromInt(originCoins)).Round(0).IntPart()
|
|
v.Coins = newPrice
|
|
}
|
|
if v.Coins == 0 {
|
|
// 根本不需要购买
|
|
return stderr.Success
|
|
}
|
|
// 判断钱包余额是否足够, 做出扣款计划
|
|
var wallet *walletmod.Wallet
|
|
wallet, err := walletmod.GetWallet(u.UID)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
if (wallet == nil || wallet.ID.IsZero()) && v.Coins > 0 {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
var orderId = primitive.NewObjectID()
|
|
var orderCreatedAt = time.Now()
|
|
|
|
if (wallet == nil || wallet.ID.IsZero()) && v.Coins == 0 {
|
|
var p walletmod.CreditPlan
|
|
var amount int64 = 0
|
|
p.Amount = &amount
|
|
wallet, err = walletmod.Credit(nil, p, u.UID)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
if wallet == nil || wallet.ID.IsZero() {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
}
|
|
plan := debitPlan(wallet, v.Coins)
|
|
if plan == nil { // 当无法做出扣款计划时, 说明余额不足
|
|
return stderr.InsufficientBalance
|
|
}
|
|
taxLevel := constant.DefaultBloggerVideoIncomeTaxLevel
|
|
/** TODO:
|
|
默认视频分成为6/4分成
|
|
*/
|
|
tl, videoDeduction, videoDeductionCount, videoDeductionPayCount := publiserTaxLevel(v.PublisherID)
|
|
if tl > 0 && tl <= 10 {
|
|
taxLevel = tl
|
|
}
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
//根据扣款计划做出扣款
|
|
wallet, err = walletmod.Debit(t, plan, u.UID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// 是否扣量以及税率计算
|
|
deducated := checkVideoDeduction(videoDeduction, videoDeductionCount, videoDeductionPayCount, v.PublisherID, 0)
|
|
ta := decimal.NewFromInt(v.Coins).Mul(decimal.NewFromInt(taxLevel).Div(decimal.NewFromInt(10)))
|
|
pIncome := decimal.NewFromInt(v.Coins).Sub(ta)
|
|
publisherIncome, _ := pIncome.Float64()
|
|
taxAmount, _ := ta.Float64()
|
|
pv := payvidlgmod.Pay4VidLog{
|
|
ID: orderId,
|
|
UID: u.UID,
|
|
VideoID: v.ID,
|
|
PlayTime: v.PlayTime,
|
|
NewsType: v.NewsType,
|
|
Coins: v.Coins,
|
|
PayMoney: v.Coins,
|
|
Tax: taxLevel,
|
|
TaxAmount: taxAmount,
|
|
Title: v.Title,
|
|
PublisherIncome: publisherIncome,
|
|
PublisherID: v.PublisherID,
|
|
Uniq: payvidlgmod.Unique(u.UID, v.ID),
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
IsVideoDeduction: deducated,
|
|
CreatedAt: orderCreatedAt,
|
|
}
|
|
if err = payvidlgmod.InsertVideoPayRecord(t, pv); err != nil {
|
|
return err
|
|
}
|
|
tl := txnmod.TransactionLog{TransNo: pv.ID,
|
|
UID: u.UID,
|
|
Amount: -v.Coins,
|
|
ActualAmount: float64(-v.Coins),
|
|
TranType: txnmod.PayVID.Key(),
|
|
TranTypeInt: int64(txnmod.PayVID),
|
|
Desc: "付费VIP折扣区视频-" + pv.Title,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
|
|
if err = txnmod.InsertTransactionLog(t, &tl); err != nil {
|
|
return err
|
|
}
|
|
// 扣量判断
|
|
if deducated {
|
|
return nil
|
|
}
|
|
//此时计算收益 收益分为两个部分 整数部分 10 小数部分 1
|
|
//计算小数部分
|
|
//与当前收益相加
|
|
pw, err := walletmod.GetWallet(v.PublisherID)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
pwPot, vidIncome := float64(0), float64(0)
|
|
if pw != nil {
|
|
pwPot = pw.IncomePot
|
|
vidIncome = pw.VidIncome
|
|
}
|
|
income := pIncome.Add(decimal.NewFromFloat(pwPot))
|
|
incomef, _ := pIncome.Add(decimal.NewFromFloat(vidIncome)).Float64()
|
|
incomeInt := income.IntPart()
|
|
incomePot, _ := income.Sub(decimal.NewFromInt(incomeInt)).Float64()
|
|
//增加钱包虚拟货币
|
|
wallet, err = walletmod.CreditIncomeBasePot(t, incomeInt, incomef, incomePot, v.PublisherID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tl = txnmod.TransactionLog{TransNo: pv.ID,
|
|
UID: v.PublisherID,
|
|
RechargeId: u.UID,
|
|
Amount: v.Coins,
|
|
ActualAmount: publisherIncome,
|
|
Tax: taxLevel,
|
|
TaxAmount: taxAmount,
|
|
TranType: txnmod.WorksIncome.Key(),
|
|
TranTypeInt: int64(txnmod.WorksIncome),
|
|
Desc: "视频收益-" + v.Title,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
//插入一收益流水
|
|
return txnmod.InsertTransactionLog(t, &tl)
|
|
}); err != nil {
|
|
log.Warn(fmt.Sprintf("productser BuyVid Transaction err [%s]", err.Error()))
|
|
return stderr.BuyFailed
|
|
}
|
|
if err = vidmod.IncPurchaseCount(v.ID); err != nil {
|
|
log.Warn("productser IncPurchaseCount err", log.Any("vid", v.ID), log.E(err))
|
|
}
|
|
|
|
return stderr.Success
|
|
}
|
|
|
|
func makeSerials(giftLeft uint32, quantity int32) (list []uint32) {
|
|
list = make([]uint32, quantity+1)
|
|
for i := int32(1); i <= quantity; i++ {
|
|
list[i] = 300 - giftLeft + uint32(i)
|
|
}
|
|
return
|
|
}
|
|
|
|
// 购买嫩模币
|
|
func BuyModel(uid uint64, req newactivity.BuyReq) stderr.Code {
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
w, err := walletmod.GetWallet(uid)
|
|
if err != nil || w == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
//一份嫩模币=10元=100金币
|
|
price := decimal.NewFromInt32(req.Quantity).Mul(decimal.NewFromInt32(100))
|
|
plan := debitPlan(w, price.IntPart())
|
|
if plan == nil {
|
|
return stderr.InsufficientBalance
|
|
}
|
|
var desc string
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
wallet, err := walletmod.Debit(t, plan, uid)
|
|
if err != nil { //扣钱
|
|
return err
|
|
}
|
|
date := timeutil.BeginningOfDay(time.Now())
|
|
if time.Now().Hour() >= 20 {
|
|
date = date.Add(24 * time.Hour)
|
|
}
|
|
giftLeft, location, err := newactivity.UpdateStock(t, req, date)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
record := newactivity.SoldRecord{
|
|
Date: date,
|
|
ModelId: req.ModelId,
|
|
UserId: uint32(uid),
|
|
UserName: u.Name,
|
|
UserLogo: u.Portrait,
|
|
Quantity: req.Quantity,
|
|
SoldOut: req.BuyOut,
|
|
}
|
|
if !req.BuyOut {
|
|
record.Serials = makeSerials(giftLeft, req.Quantity)
|
|
}
|
|
id, err := newactivity.InsertSoldLog(t, &record)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
history := prdcthsomod.ProductHistory{ //插入商品购买记录
|
|
ID: primitive.NewObjectID(),
|
|
UID: uid,
|
|
ProductID: primitive.NilObjectID,
|
|
Name: "嫩模币",
|
|
Amount: plan.Amount,
|
|
Income: plan.Income,
|
|
ProductType: prdcthsomod.MODEL,
|
|
SysType: u.SysType,
|
|
}
|
|
if err = prdcthsomod.InsertProductHistory(t, &history); err != nil {
|
|
return err
|
|
}
|
|
desc = "参与活动:嫩模之夜," + makeDesc(date, location, req)
|
|
txnLog := txnmod.TransactionLog{UID: uid,
|
|
Amount: -price.IntPart(),
|
|
ActualAmount: float64(-price.IntPart()),
|
|
TranType: txnmod.NengModel.Key(),
|
|
TranTypeInt: int64(txnmod.NengModel),
|
|
TransNo: id,
|
|
Desc: desc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
return txnmod.InsertTransactionLog(t, &txnLog)
|
|
}); err != nil {
|
|
log.Warn(fmt.Sprintf("productser BuyModel Transaction err %s", err.Error()))
|
|
return stderr.BuyFailed
|
|
}
|
|
return stderr.Success
|
|
}
|
|
|
|
func makeDesc(date time.Time, location string, req newactivity.BuyReq) (desc string) {
|
|
desc = "赠送" + date.Format("0102") + "期,"
|
|
desc += location + strconv.Itoa(int(req.ModelId))
|
|
desc += "号嫩模" + strconv.Itoa(int(req.Quantity)) + "份礼物"
|
|
return
|
|
}
|
|
|
|
// 使用机器人购买嫩模
|
|
func BuyModelFakeUser(uid uint64, req newactivity.BuyReq) stderr.Code {
|
|
wallet, err := walletmod.GetWallet(uid)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
//一份嫩模币=10元=100金币
|
|
price := decimal.NewFromInt32(req.Quantity).Mul(decimal.NewFromInt32(100))
|
|
var desc string
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
date := timeutil.BeginningOfDay(time.Now())
|
|
if time.Now().Hour() >= 20 {
|
|
date = date.Add(24 * time.Hour)
|
|
}
|
|
giftLeft, location, err := newactivity.UpdateStock(t, req, date)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
record := newactivity.SoldRecord{
|
|
Date: date,
|
|
ModelId: req.ModelId,
|
|
UserId: uint32(uid),
|
|
UserName: u.Name,
|
|
UserLogo: u.Portrait,
|
|
Quantity: req.Quantity,
|
|
SoldOut: req.BuyOut,
|
|
}
|
|
if !req.BuyOut {
|
|
record.Serials = makeSerials(giftLeft, req.Quantity)
|
|
}
|
|
id, err := newactivity.InsertSoldLog(t, &record)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
history := prdcthsomod.ProductHistory{ //插入商品购买记录
|
|
ID: primitive.NewObjectID(),
|
|
UID: uid,
|
|
ProductID: primitive.NilObjectID,
|
|
Name: "嫩模币",
|
|
Amount: price.IntPart(),
|
|
Income: 0,
|
|
ProductType: prdcthsomod.MODEL,
|
|
SysType: u.SysType,
|
|
}
|
|
if err = prdcthsomod.InsertProductHistory(t, &history); err != nil {
|
|
return err
|
|
}
|
|
desc = "参与活动:嫩模之夜," + makeDesc(date, location, req)
|
|
txnLog := txnmod.TransactionLog{UID: uid,
|
|
Amount: -price.IntPart(),
|
|
ActualAmount: float64(-price.IntPart()),
|
|
TranType: txnmod.NengModel.Key(),
|
|
TranTypeInt: int64(txnmod.NengModel),
|
|
TransNo: id,
|
|
Desc: desc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
return txnmod.InsertTransactionLog(t, &txnLog)
|
|
}); err != nil {
|
|
log.Warn(fmt.Sprintf("productser BuyModel Transaction err %s", err.Error()))
|
|
return stderr.BuyFailed
|
|
}
|
|
return stderr.Success
|
|
}
|
|
|
|
// 扣除指定的游戏币
|
|
func DeductGameCoins(uid uint64, deductCoin int64) stderr.Code {
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
w, err := walletmod.GetWallet(uid)
|
|
if err != nil || w == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
plan := debitPlan(w, deductCoin)
|
|
if plan == nil {
|
|
return stderr.InsufficientBalance
|
|
}
|
|
var desc string
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
wallet, err := walletmod.Debit(t, plan, uid)
|
|
if err != nil { //扣钱
|
|
return err
|
|
}
|
|
history := prdcthsomod.ProductHistory{ //插入商品购买记录
|
|
ID: primitive.NewObjectID(),
|
|
UID: uid,
|
|
ProductID: primitive.NilObjectID,
|
|
Name: "游戏币",
|
|
Amount: plan.Amount,
|
|
Income: plan.Income,
|
|
ProductType: prdcthsomod.GAME,
|
|
SysType: u.SysType,
|
|
}
|
|
if err = prdcthsomod.InsertProductHistory(t, &history); err != nil {
|
|
return err
|
|
}
|
|
txnLog := txnmod.TransactionLog{UID: uid,
|
|
Amount: -deductCoin,
|
|
ActualAmount: float64(-deductCoin),
|
|
TranType: txnmod.GameCoin.Key(),
|
|
TranTypeInt: int64(txnmod.GameCoin),
|
|
Desc: desc,
|
|
TransNo: history.ID,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
return txnmod.InsertTransactionLog(t, &txnLog)
|
|
}); err != nil {
|
|
log.Warn(fmt.Sprintf("productser DeductGameCoins Transaction err %s", err.Error()))
|
|
return stderr.BuyFailed
|
|
}
|
|
return stderr.Success
|
|
}
|
|
|
|
func BuyMeetingCard(uid uint64, productID primitive.ObjectID, sys string, ua ua.UA, ip 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.ErrNetWorkBusy
|
|
}
|
|
w, err := walletmod.GetWallet(uid)
|
|
if err != nil || w == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
|
|
var orderId = primitive.NewObjectID()
|
|
var orderCreatedAt = time.Now()
|
|
|
|
plan := debitPlan(w, p.DiscountedPrice)
|
|
if plan == nil {
|
|
return stderr.InsufficientBalance
|
|
}
|
|
if p.GiveCoin > 0 {
|
|
plan.Amount = plan.Amount + p.GiveCoin
|
|
}
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
discDoc := u.DiscDoc
|
|
wallet, err := walletmod.Debit(t, plan, uid)
|
|
if err != nil { //扣钱
|
|
return err
|
|
}
|
|
|
|
history := prdcthsomod.ProductHistory{ //插入商品购买记录
|
|
ID: orderId,
|
|
UID: uid,
|
|
ProductID: productID,
|
|
Name: p.Name,
|
|
Amount: plan.Amount,
|
|
Income: plan.Income,
|
|
ProductType: prdcthsomod.MeetingCard,
|
|
DiscDoc: discDoc,
|
|
SysType: u.SysType,
|
|
ProductSnapShot: p,
|
|
CreatedAt: orderCreatedAt,
|
|
}
|
|
if err = prdcthsomod.InsertProductHistory(t, &history); err != nil {
|
|
return err
|
|
}
|
|
txnLog := txnmod.TransactionLog{
|
|
UID: uid,
|
|
Amount: -p.DiscountedPrice,
|
|
ActualAmount: float64(-p.DiscountedPrice),
|
|
TranType: txnmod.MeetingCard.Key(),
|
|
TranTypeInt: int64(txnmod.MeetingCard),
|
|
TransNo: history.ID,
|
|
Desc: "购买-" + p.Name,
|
|
DiscDoc: discDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
|
|
return txnmod.InsertTransactionLog(t, &txnLog)
|
|
}); err != nil {
|
|
log.Warn(fmt.Sprintf("productser buy meetingcard transaction err %s", err.Error()))
|
|
return stderr.BuyFailed
|
|
}
|
|
|
|
return stderr.Success
|
|
}
|
|
|
|
func BuyMeetingCard_d(t *db.MongoTool, uid uint64, p productmod.Product) error {
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return errors.New("user is null")
|
|
}
|
|
w, err := walletmod.GetWallet(uid)
|
|
if err != nil || w == nil {
|
|
return err
|
|
}
|
|
history := prdcthsomod.ProductHistory{ //插入商品购买记录
|
|
ID: primitive.NewObjectID(),
|
|
UID: uid,
|
|
ProductID: p.ID,
|
|
Name: p.Name,
|
|
Amount: p.DiscountedPrice,
|
|
ProductType: prdcthsomod.MeetingCard,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
ProductSnapShot: &p,
|
|
}
|
|
if err = prdcthsomod.InsertProductHistory(t, &history); err != nil {
|
|
return err
|
|
}
|
|
txnLog := txnmod.TransactionLog{
|
|
UID: uid,
|
|
Amount: -p.DiscountedPrice,
|
|
ActualAmount: float64(-p.DiscountedPrice),
|
|
TranType: txnmod.MeetingCard.Key(),
|
|
TranTypeInt: int64(txnmod.MeetingCard),
|
|
TransNo: history.ID,
|
|
Desc: "购买-" + p.Name,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: w.RealAmount(),
|
|
CurrencyType: txnmod.CurrencyTypeCash,
|
|
}
|
|
if err = txnmod.InsertTransactionLog(t, &txnLog); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func BuyOtherCard(uid uint64, productID primitive.ObjectID, sys string, ua ua.UA, ip 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.ErrNetWorkBusy
|
|
}
|
|
w, err := walletmod.GetWallet(uid)
|
|
if err != nil || w == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
|
|
var orderId = primitive.NewObjectID()
|
|
var orderCreatedAt = time.Now()
|
|
|
|
plan := debitPlan(w, p.DiscountedPrice)
|
|
if plan == nil {
|
|
return stderr.InsufficientBalance
|
|
}
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
discDoc := u.DiscDoc
|
|
wallet, err := walletmod.Debit(t, plan, uid)
|
|
if err != nil { //扣钱
|
|
return err
|
|
}
|
|
|
|
history := prdcthsomod.ProductHistory{ //插入商品购买记录
|
|
ID: orderId,
|
|
UID: uid,
|
|
ProductID: productID,
|
|
Name: p.Name,
|
|
Amount: plan.Amount,
|
|
Income: plan.Income,
|
|
ProductType: prdcthsomod.OTHER,
|
|
DiscDoc: discDoc,
|
|
SysType: u.SysType,
|
|
ProductSnapShot: p,
|
|
CreatedAt: orderCreatedAt,
|
|
}
|
|
if err = prdcthsomod.InsertProductHistory(t, &history); err != nil {
|
|
return err
|
|
}
|
|
txnLog := txnmod.TransactionLog{
|
|
UID: uid,
|
|
Amount: -p.DiscountedPrice,
|
|
ActualAmount: float64(-p.DiscountedPrice),
|
|
TranType: txnmod.Other.Key(),
|
|
TranTypeInt: int64(txnmod.Other),
|
|
TransNo: history.ID,
|
|
Desc: "购买-" + p.Name,
|
|
DiscDoc: discDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
|
|
return txnmod.InsertTransactionLog(t, &txnLog)
|
|
}); err != nil {
|
|
log.Warn(fmt.Sprintf("productser buy otherCard transaction err %s", err.Error()))
|
|
return stderr.BuyFailed
|
|
}
|
|
|
|
return stderr.Success
|
|
}
|
|
|
|
func BuyOtherCard_d(t *db.MongoTool, uid uint64, p productmod.Product) error {
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return errors.New("user is null")
|
|
}
|
|
w, err := walletmod.GetWallet(uid)
|
|
if err != nil || w == nil {
|
|
return err
|
|
}
|
|
history := prdcthsomod.ProductHistory{ //插入商品购买记录
|
|
ID: primitive.NewObjectID(),
|
|
UID: uid,
|
|
ProductID: p.ID,
|
|
Name: p.Name,
|
|
Amount: p.DiscountedPrice,
|
|
ProductType: prdcthsomod.OTHER,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
ProductSnapShot: &p,
|
|
}
|
|
if err = prdcthsomod.InsertProductHistory(t, &history); err != nil {
|
|
return err
|
|
}
|
|
txnLog := txnmod.TransactionLog{
|
|
UID: uid,
|
|
Amount: -p.DiscountedPrice,
|
|
ActualAmount: float64(-p.DiscountedPrice),
|
|
TranType: txnmod.Other.Key(),
|
|
TranTypeInt: int64(txnmod.Other),
|
|
TransNo: history.ID,
|
|
Desc: "购买-" + p.Name,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: w.RealAmount(),
|
|
CurrencyType: txnmod.CurrencyTypeCash,
|
|
}
|
|
if err = txnmod.InsertTransactionLog(t, &txnLog); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func CountdownTiroCard(uid uint64) (countdownSec int64, err error) {
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if u == nil {
|
|
err = errors.New("invalid user")
|
|
return
|
|
}
|
|
var newUserHour int64 = 24
|
|
//查询数据库里面的字段值直接赋予给newUserHour
|
|
//data, err := vipconfigmod.FindOne()
|
|
//if err != nil {
|
|
// return
|
|
//}
|
|
//if data.TimeConfig > 0 {
|
|
// newUserHour = data.TimeConfig
|
|
//}
|
|
/*data, _ := productmod.FindByProductType(commod.NEWUSERCard)
|
|
if len(data) > 0 {
|
|
newUserHour = data[0].ShowCountdownTime
|
|
}*/
|
|
now := time.Now()
|
|
countdownSec = u.CreatedAt.Add(time.Duration(newUserHour)*time.Hour).Unix() - now.Unix()
|
|
if countdownSec < 0 {
|
|
countdownSec = 0
|
|
}
|
|
return
|
|
}
|
|
|
|
func BuyAudioBook(uid uint64, lid primitive.ObjectID, chapterID string, ua ua.UA, ip string) stderr.Code {
|
|
l, err := audiobookmod.GetByID(lid)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
pab, err := payaudiobookmod.FindByUIDAndObjID(uid, lid)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
desc := "购买有声小说:" + l.Title + "."
|
|
var price int64 = 0
|
|
episodeNumberSet := make([]int, 0)
|
|
if chapterID == "" {
|
|
desc += "(全购:"
|
|
for i := range l.ContentSet {
|
|
if l.ContentSet[i].ListenPermission == audiobookmod.ListenPermissionBuy && (!sli.ContainsInt(pab.EpisodeNumberSet, l.ContentSet[i].EpisodeNumber)) {
|
|
price += l.ContentSet[i].Price
|
|
desc += l.ContentSet[i].Name + "."
|
|
episodeNumberSet = append(episodeNumberSet, l.ContentSet[i].EpisodeNumber)
|
|
}
|
|
}
|
|
desc += ")."
|
|
} else {
|
|
episodeNumber, err := strconv.Atoi(chapterID)
|
|
if err != nil {
|
|
return stderr.ErrParamError
|
|
}
|
|
for i := range l.ContentSet {
|
|
if l.ContentSet[i].EpisodeNumber == episodeNumber {
|
|
if l.ContentSet[i].ListenPermission == audiobookmod.ListenPermissionBuy && !sli.ContainsInt(pab.EpisodeNumberSet, episodeNumber) {
|
|
price += l.ContentSet[i].Price
|
|
desc += "(单购:" + l.ContentSet[i].Name + ".)"
|
|
episodeNumberSet = append(episodeNumberSet, l.ContentSet[i].EpisodeNumber)
|
|
}
|
|
break
|
|
}
|
|
|
|
}
|
|
}
|
|
if price == 0 {
|
|
return stderr.RepeatPurchase
|
|
}
|
|
w, err := walletmod.GetWallet(uid)
|
|
if err != nil || w == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
plan := debitPlan(w, price)
|
|
if plan == nil {
|
|
return stderr.InsufficientBalance
|
|
}
|
|
pid := l.ID.Hex()
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
wallet, err := walletmod.Debit(t, plan, uid)
|
|
if err != nil { //扣钱
|
|
return err
|
|
}
|
|
txnLog := txnmod.TransactionLog{
|
|
UID: uid,
|
|
ProductID: &pid,
|
|
Amount: -price,
|
|
ActualAmount: float64(-price),
|
|
TranType: txnmod.AudioBook.Key(),
|
|
TranTypeInt: int64(txnmod.AudioBook),
|
|
Desc: desc,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
if err = txnmod.InsertTransactionLog(t, &txnLog); err != nil {
|
|
return err
|
|
}
|
|
return payaudiobookmod.Insert(payaudiobookmod.PayAudioBookHistory{
|
|
UID: uid,
|
|
EpisodeNumberSet: episodeNumberSet,
|
|
ObjID: lid,
|
|
}, t)
|
|
}); err != nil {
|
|
log.Warn(fmt.Sprintf("productser BuyVIP Transaction err %s", err.Error()))
|
|
return stderr.BuyFailed
|
|
}
|
|
return stderr.Success
|
|
}
|
|
|
|
func checkVideoDisciountRenew(videoDiscLog videodiscountmod.VideoDiscountLog, p productmod.Product) time.Time {
|
|
duration := time.Hour * 24 * time.Duration(p.Duration) // Duration以天为单位
|
|
now := time.Now()
|
|
if videoDiscLog.Expiration.After(now) {
|
|
return videoDiscLog.Expiration.Add(duration)
|
|
}
|
|
return now.Add(duration)
|
|
}
|
|
|
|
func checkVideoFreeCardRenew(user usermod.User, product productmod.Product, t time.Time) time.Time {
|
|
duration := time.Hour * 24 * time.Duration(product.Duration)
|
|
if user.VideoFreeExpiration != nil && user.VideoFreeExpiration.After(t) {
|
|
return user.VideoFreeExpiration.Add(duration)
|
|
}
|
|
return t.Add(duration)
|
|
}
|
|
|
|
func BuyVideoFreeCard(uid uint64, pid primitive.ObjectID, sys string, ua ua.UA, ip string) stderr.Code {
|
|
p, err := productmod.FindProduct(pid, sys)
|
|
if err != nil || p == nil {
|
|
return stderr.ErrParamError
|
|
}
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
w, err := walletmod.GetWallet(uid)
|
|
if err != nil || w == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
|
|
var orderId = primitive.NewObjectID()
|
|
var orderCreatedAt = time.Now()
|
|
|
|
expiration := checkVideoFreeCardRenew(*u, *p, time.Now())
|
|
if err != nil {
|
|
return stderr.ErrParamError
|
|
}
|
|
plan := debitPlan(w, p.DiscountedPrice)
|
|
if plan == nil {
|
|
return stderr.InsufficientBalance
|
|
}
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
discDoc := u.DiscDoc
|
|
wallet, err := walletmod.Debit(t, plan, uid)
|
|
if err != nil { //扣钱
|
|
return err
|
|
}
|
|
|
|
history := prdcthsomod.ProductHistory{ //插入商品购买记录
|
|
ID: orderId,
|
|
UID: uid,
|
|
ProductID: pid,
|
|
Name: p.Name,
|
|
Amount: plan.Amount,
|
|
Income: plan.Income,
|
|
ProductType: prdcthsomod.VideoFreeCard,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
ProductSnapShot: p,
|
|
CreatedAt: orderCreatedAt,
|
|
}
|
|
if err = prdcthsomod.InsertProductHistory(t, &history); err != nil {
|
|
return err
|
|
}
|
|
txnLog := txnmod.TransactionLog{
|
|
UID: uid,
|
|
Amount: -p.DiscountedPrice,
|
|
ActualAmount: float64(-p.DiscountedPrice),
|
|
TranType: txnmod.VideoFreeCard.Key(),
|
|
TranTypeInt: int64(txnmod.VideoFreeCard),
|
|
Desc: "购买-" + p.Name,
|
|
DiscDoc: discDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
TransNo: history.ID,
|
|
}
|
|
if err = txnmod.InsertTransactionLog(t, &txnLog); err != nil {
|
|
return err
|
|
}
|
|
|
|
userSet := usermod.UserSelector{
|
|
VideoFreeExpiration: &expiration,
|
|
}
|
|
_, _ = usermod.UpdateTrans(t, uid, userSet)
|
|
return nil
|
|
}); err != nil {
|
|
log.Error("BuyVideoFreeCard", log.Any("uid", uid), log.Any("productID", pid), log.Any("sys", sys))
|
|
return stderr.BuyFailed
|
|
}
|
|
|
|
return stderr.Success
|
|
}
|
|
|
|
func BuyVideoDiscountCard(uid uint64, pid primitive.ObjectID, sys string, ua ua.UA, ip string) stderr.Code {
|
|
p, err := productmod.FindProduct(pid, sys)
|
|
if err != nil || p == nil {
|
|
return stderr.ErrParamError
|
|
}
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
w, err := walletmod.GetWallet(uid)
|
|
if err != nil || w == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
|
|
var orderId = primitive.NewObjectID()
|
|
var orderCreatedAt = time.Now()
|
|
|
|
videoDiscountLog, err := videodiscountmod.GetByUID(uid)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
expiration := checkVideoDisciountRenew(videoDiscountLog, *p)
|
|
plan := debitPlan(w, p.DiscountedPrice)
|
|
if plan == nil {
|
|
return stderr.InsufficientBalance
|
|
}
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
wallet, err := walletmod.Debit(t, plan, uid) // 尝试直接从钱包扣钱
|
|
if err != nil { // 钱包余额不足
|
|
return err
|
|
}
|
|
|
|
history := prdcthsomod.ProductHistory{ //插入商品购买记录
|
|
ID: orderId,
|
|
UID: uid,
|
|
ProductID: pid,
|
|
Name: p.Name,
|
|
Amount: plan.Amount,
|
|
Income: plan.Income,
|
|
ProductType: prdcthsomod.VideoDiscount,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
ProductSnapShot: p,
|
|
CreatedAt: orderCreatedAt,
|
|
}
|
|
if err = prdcthsomod.InsertProductHistory(t, &history); err != nil {
|
|
return err
|
|
}
|
|
txnLog := txnmod.TransactionLog{
|
|
UID: uid,
|
|
Amount: -p.DiscountedPrice,
|
|
ActualAmount: float64(-p.DiscountedPrice),
|
|
TranType: txnmod.VideoDiscount.Key(),
|
|
TranTypeInt: int64(txnmod.VideoDiscount),
|
|
TransNo: history.ID,
|
|
Desc: "购买-" + p.Name,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
}
|
|
if err = txnmod.InsertTransactionLog(t, &txnLog); err != nil {
|
|
return err
|
|
}
|
|
set := videodiscountmod.EditSelector{
|
|
UID: &uid,
|
|
Expiration: &expiration,
|
|
VideoDiscount: &p.VideoDiscount,
|
|
}
|
|
|
|
return videodiscountmod.Upsert(t, &set)
|
|
}); err != nil {
|
|
log.Error("BuyVideoDiscountCard", log.Any("uid", uid), log.Any("productID", pid), log.Any("sys", sys))
|
|
return stderr.BuyFailed
|
|
}
|
|
|
|
return stderr.Success
|
|
}
|
|
|
|
func BuyVideoDiscountCard_d(t *db.MongoTool, uid uint64, p productmod.Product) error {
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
w, err := walletmod.GetWallet(uid)
|
|
if err != nil || w == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
videoDiscountLog, err := videodiscountmod.GetByUID(uid)
|
|
if err != nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
expiration := checkVideoDisciountRenew(videoDiscountLog, p)
|
|
discountSet := videodiscountmod.EditSelector{
|
|
UID: &uid,
|
|
Expiration: &expiration,
|
|
VideoDiscount: &p.VideoDiscount,
|
|
}
|
|
if err = videodiscountmod.Upsert(t, &discountSet); err != nil {
|
|
return err
|
|
}
|
|
history := prdcthsomod.ProductHistory{
|
|
ID: primitive.NewObjectID(),
|
|
UID: uid,
|
|
ProductID: p.ID,
|
|
Name: p.Name,
|
|
Amount: p.DiscountedPrice,
|
|
ProductType: productmod.VideoDiscount,
|
|
SysType: u.SysType,
|
|
DiscDoc: u.DiscDoc,
|
|
}
|
|
if err = prdcthsomod.InsertProductHistory(t, &history); err != nil {
|
|
return err
|
|
}
|
|
txnLog := txnmod.TransactionLog{
|
|
UID: uid,
|
|
Amount: -p.DiscountedPrice,
|
|
ActualAmount: float64(-p.DiscountedPrice),
|
|
TranType: txnmod.VideoDiscount.Key(),
|
|
TranTypeInt: int64(txnmod.VideoDiscount),
|
|
Desc: "购买-" + p.Name,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: w.RealAmount(),
|
|
CurrencyType: txnmod.CurrencyTypeCash,
|
|
TransNo: history.ID,
|
|
}
|
|
return txnmod.InsertTransactionLog(t, &txnLog)
|
|
}
|
|
|
|
// BuyVideoFreeCard_d 购买视频免费卡,兼容旧版客户端请求
|
|
func BuyVideoFreeCard_d(t *db.MongoTool, uid uint64, p productmod.Product) error {
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return stderr.ErrNetWorkBusy
|
|
}
|
|
now := time.Now()
|
|
expiration := checkVideoFreeCardRenew(*u, p, now)
|
|
userSet := usermod.UserSelector{
|
|
VideoFreeExpiration: &expiration,
|
|
}
|
|
if _, err = usermod.UpdateTrans(t, uid, userSet); err != nil {
|
|
return err
|
|
}
|
|
history := prdcthsomod.ProductHistory{
|
|
ID: primitive.NewObjectID(),
|
|
UID: uid,
|
|
ProductID: p.ID,
|
|
Name: p.Name,
|
|
Amount: p.DiscountedPrice,
|
|
ProductType: prdcthsomod.VideoFreeCard,
|
|
SysType: u.SysType,
|
|
DiscDoc: u.DiscDoc,
|
|
}
|
|
if err = prdcthsomod.InsertProductHistory(t, &history); err != nil {
|
|
return err
|
|
}
|
|
wallet, err := walletmod.GetWallet(uid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
txnLog := txnmod.TransactionLog{
|
|
UID: uid,
|
|
Amount: -p.DiscountedPrice,
|
|
ActualAmount: float64(-p.DiscountedPrice),
|
|
TranType: txnmod.VideoFreeCard.Key(),
|
|
TranTypeInt: int64(txnmod.VideoFreeCard),
|
|
Desc: "购买-" + p.Name,
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: walletmod.GetRealAmount(wallet),
|
|
CurrencyType: txnmod.CurrencyTypeCash,
|
|
TransNo: history.ID,
|
|
}
|
|
return txnmod.InsertTransactionLog(t, &txnLog)
|
|
}
|
|
|
|
func DelBroughtHistory(videoID primitive.ObjectID, uid uint64) error {
|
|
return payvidlgmod.DelVideoPayRecord(videoID, uid)
|
|
}
|
|
|
|
func GetCouponDetail(uid uint64, productID string, productType commod.ProductType, sys string) (ActivityCouponDetailResponse, stderr.Code) {
|
|
var (
|
|
couponType int
|
|
goodsType int
|
|
isAmountPay bool
|
|
)
|
|
data := ActivityCouponDetailResponse{}
|
|
id, err := primitive.ObjectIDFromHex(productID)
|
|
if err != nil {
|
|
return data, stderr.ErrParamError
|
|
}
|
|
switch productType {
|
|
case commod.VIP, commod.NEWUSERCard:
|
|
// 获取商品信息
|
|
productDetail, err := productmod.FindProduct(id, sys)
|
|
if err != nil {
|
|
return data, stderr.ErrDbQueryError
|
|
}
|
|
if productDetail.IsAmountPay {
|
|
isAmountPay = true
|
|
}
|
|
data.OriginalPrice = productDetail.OriginalPrice
|
|
data.DiscountedPrice = productDetail.DiscountedPrice
|
|
couponType = int(prizemod.VIPDiscount)
|
|
goodsType = int(backpackmod.VIPDiscount)
|
|
default:
|
|
return data, stderr.ErrParamError
|
|
}
|
|
// 获取所有优惠卷
|
|
couponList, err := prizemod.GetCouponList(couponType)
|
|
if err != nil {
|
|
return data, stderr.ErrDbQueryError
|
|
}
|
|
// 获取个人拥有的优惠卷
|
|
goodsList, err := backpackmod.GetCouponListByUID(uid, goodsType)
|
|
if err != nil {
|
|
return data, stderr.ErrDbQueryError
|
|
}
|
|
// 会员卡(金币购买)
|
|
if isAmountPay {
|
|
for _, v := range couponList {
|
|
couponDetail := ActivityCoupon{}
|
|
if productType == commod.VIP {
|
|
couponDetail.DiscountedPrice = decimal.NewFromInt(v.Value).Mul(decimal.NewFromInt(data.DiscountedPrice)).Div(decimal.NewFromInt(10)).IntPart()
|
|
} else {
|
|
couponDetail.DiscountedPrice = decimal.NewFromInt(v.Value).Mul(decimal.NewFromInt(data.OriginalPrice)).Div(decimal.NewFromInt(10)).IntPart()
|
|
}
|
|
couponDetail.Name = v.Name
|
|
for _, gv := range goodsList {
|
|
if v.Value == gv.GoodsValue {
|
|
if couponDetail.ID.IsZero() {
|
|
couponDetail.ID = gv.ID
|
|
}
|
|
couponDetail.Count++
|
|
}
|
|
}
|
|
data.CouponList = append(data.CouponList, couponDetail)
|
|
}
|
|
}
|
|
if data.DiscountedPrice < data.OriginalPrice {
|
|
data.IsDiscounted = true
|
|
}
|
|
return data, stderr.Success
|
|
}
|
|
|
|
// 视频扣量校验
|
|
func checkVideoDeduction(videoDeduction float64, videoDeductionCount, videoDeductionPayCount int, uid uint64, goldVideoCouponNum int) bool {
|
|
//使用金币视频抵用券, 则直接返回扣量, 且不计入博主视频的购买数量和扣量次数
|
|
if goldVideoCouponNum > 0 {
|
|
log.Info("checkVideoDeduction, goldVideoCouponNum---100%扣量", log.Any("uid", uid))
|
|
return true
|
|
}
|
|
//根据用户扣量比率videoDeduction 来进行计算.
|
|
if videoDeduction == 0 { // 若扣量比例为0 则直接返回不扣量, 且不计入博主视频的购买数量和扣量次数
|
|
log.Info("checkVideoDeduction---不扣量", log.Any("uid", uid))
|
|
return false
|
|
}
|
|
// 计算是否扣量:
|
|
deduct := func() bool {
|
|
if videoDeduction >= 10 || videoDeductionCount == 0 {
|
|
return true // 若扣量比例为100% 或视频没有扣量次数, 则扣量
|
|
}
|
|
// 按比例计算扣量
|
|
deduction := (float64(videoDeductionCount) / (float64(videoDeductionPayCount + 1))) * 10
|
|
if deduction < videoDeduction { // 若当前扣量比例低于配置的扣量比例, 则扣量
|
|
log.Info("checkVideoDeduction---扣量", log.Any("videoDeduction", videoDeduction), log.Any("deduction", deduction), log.Any("uid", uid))
|
|
return true
|
|
}
|
|
log.Info("checkVideoDeduction---不扣量", log.Any("videoDeduction", videoDeduction), log.Any("deduction", deduction), log.Any("uid", uid))
|
|
return false
|
|
}()
|
|
//更新博主视频的购买数量和扣量次数
|
|
_ = usermod.IncVideoDeduction(uid, deduct)
|
|
return deduct
|
|
}
|
|
|
|
func GetCoin(uid uint64) (data interface{}, code stderr.Code) {
|
|
//获取用户信息
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return nil, stderr.ErrNetWorkBusy
|
|
}
|
|
//获取金币月卡产品信息---根据产品类型获取
|
|
p, err := productmod.FindByProductType(commod.CoinMonthCard)
|
|
if err != nil || p == nil {
|
|
return nil, stderr.ErrParamError
|
|
}
|
|
w, err := walletmod.GetWallet(uid)
|
|
if err != nil || w == nil {
|
|
return nil, stderr.ErrNetWorkBusy
|
|
}
|
|
//检查vip过期时间_判断用户vip是否过期
|
|
if !u.CoinMouthExpireDate.After(time.Now()) {
|
|
return nil, stderr.ExpiredVip
|
|
}
|
|
txnLogs := make([]txnmod.TransactionLog, 0)
|
|
if err = appg.VideoDB.Trans(func(t *db.MongoTool) error {
|
|
//金币月卡赠送金币
|
|
if len(p) > 0 && p[0].EveryDayGiveCoin > 0 {
|
|
if _, err = walletmod.CreditAmount(t, p[0].EveryDayGiveCoin, uid); err != nil {
|
|
return err
|
|
}
|
|
//插入购买金币月卡怎送金币流水
|
|
giveLog := txnmod.TransactionLog{UID: uid,
|
|
Amount: p[0].EveryDayGiveCoin,
|
|
ActualAmount: float64(p[0].EveryDayGiveCoin),
|
|
TranType: txnmod.VipCardGive.Key(),
|
|
TranTypeInt: int64(txnmod.VipCardGive),
|
|
TransNo: p[0].ID,
|
|
Desc: "金币月卡每日赠送金币",
|
|
DiscDoc: u.DiscDoc,
|
|
SysType: u.SysType,
|
|
RealAmount: w.RealAmount(),
|
|
}
|
|
txnLogs = append(txnLogs, giveLog)
|
|
if err = txnmod.InsertTransactionLog(t, &giveLog); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
|
|
}); err != nil {
|
|
log.Warn(fmt.Sprintf("productser BuyModel Transaction err %s", err.Error()))
|
|
return nil, stderr.BuyFailed
|
|
}
|
|
return "success", stderr.Success
|
|
}
|
|
|
|
func GetAwVipInfo(uid uint64) (data *productmod.RespVipInfoList, code stderr.Code) {
|
|
var res productmod.RespVipInfoList
|
|
//获取用户信息
|
|
u, err := usermod.FindUserByUID(uid)
|
|
if err != nil || u == nil {
|
|
return nil, stderr.ErrNetWorkBusy
|
|
}
|
|
|
|
str, err := appg.Redis.Get(redisconst.GetAwVipInfo)
|
|
if err != nil {
|
|
log.Warn(fmt.Sprintf("用户ID:%d;缓存获取会员卡列表信息异常:%v", uid, err))
|
|
}
|
|
|
|
if str != nil {
|
|
if err = json.Unmarshal([]byte(*str), &res); err == nil {
|
|
return &res, stderr.Success
|
|
}
|
|
log.Warn(fmt.Sprintf("用户ID:%d;解析缓存数据异常:%v", uid, err))
|
|
}
|
|
productList, err := productmod.FindByVipLevel(productmod.LevelThree)
|
|
if err != nil || productList == nil {
|
|
return nil, stderr.ErrParamError
|
|
}
|
|
|
|
if len(productList) <= 0 {
|
|
return &res, stderr.Success
|
|
}
|
|
|
|
if len(productList) > 0 {
|
|
for _, p := range productList {
|
|
res.List = append(res.List, productmod.ProductList{
|
|
ID: p.ID.Hex(),
|
|
Name: p.Name,
|
|
})
|
|
}
|
|
}
|
|
|
|
common.Go(func() {
|
|
if res.List != nil || len(res.List) > 0 {
|
|
d, err := json.Marshal(res)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if err = appg.Redis.Set(redisconst.GetAwVipInfo, d, 5*time.Minute); err != nil {
|
|
log.Warn(fmt.Sprintf("用户ID:%d;保存缓存数据异常:%v", uid, err))
|
|
}
|
|
}
|
|
})
|
|
return &res, stderr.Success
|
|
}
|
|
|
|
//// GetRecommendVip 获取推荐展示的vip卡
|
|
//func GetRecommendVip() (data *productmod.Product, err error) {
|
|
// _, err = cachev2.Classes().CacheTime(redisconst.RecommendVipExpire).Key(redisconst.RecommendVip).ResBind(&data).Cache(productmod.GetRecommendVip)
|
|
// if err != nil {
|
|
// log.Error("cachev2 productmod.FindOne fail", log.E(err))
|
|
// return
|
|
// }
|
|
// return
|
|
//}
|