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

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
//}