package active2023ser import ( "errors" "fmt" "time" "91porn-server/app/appg" "91porn-server/common/db" "91porn-server/models/commod" "91porn-server/models/v/active2023mod" "91porn-server/models/v/goldextramod" "91porn-server/models/v/prdcthsomod" "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/walletmod" "go.mongodb.org/mongo-driver/bson/primitive" ) func Lottery(userInfo *usermod.User, req LotteryReq) error { wallet, err := walletmod.GetWallet(userInfo.UID) if err != nil { return err } if wallet == nil { return errors.New("钱包信息未找到") } isLottery := true // 默认为抽奖 desc := "2023新春抽奖" if req.Gold == 0 && req.Count == 0 { // 不使用免费抽奖次数又不消耗金币, 则为领取保底福利 isLottery = false desc = "2023新春福利" } return appg.VideoDB.Trans(func(t *db.MongoTool) error { var transactionLogs []txnmod.TransactionLog // 用户所有抽奖花费金, 获奖金币以及游戏币发放记录 if isLottery { if req.Gold == 0 { // 使用免费抽奖次数 if err = active2023mod.UseLotteryCountFree(t, int64(userInfo.UID), int64(req.Count)); err != nil { return err } } else { // 使用金币抽奖 if err = walletmod.DebitAmountDefault(t, int64(userInfo.UID), int64(req.Gold)); err != nil { return err } wallet.Amount -= int64(req.Gold) if err = active2023mod.UserLotteryCountPay(t, int64(userInfo.UID), int64(req.Count)); err != nil { return err } transactionType := txnmod.Active2023Cost transactionLogs = []txnmod.TransactionLog{txnmod.TransactionLog{ TransNo: primitive.NewObjectID(), UID: userInfo.UID, Amount: -int64(req.Gold), ActualAmount: -float64(req.Gold), TranType: transactionType.Key(), TranTypeInt: int64(transactionType), Desc: fmt.Sprintf("%s-金币[%d个]", transactionType.Key(), req.Gold), RealAmount: walletmod.GetRealAmount(wallet), }} } } var totalGold, totalVipDates uint64 // totalGold: 用户总获奖金币数; totalVipDates: 用户总获奖vip天数 var pzs []active2023mod.Prize // 用户所有奖励, 用以存库 var videoCoupons []videocoupon.UserGoldVideoCoupon // 用户所有获视频折扣券 var goldExtras []goldextramod.GoldExtra // 用户所有获奖金币加购券 var videoDiscounts []productmod.Product // 用户所有获奖视频折扣券 var vipCards []productmod.Product // 用户所有获奖vip卡 now := time.Now() pzs = make([]active2023mod.Prize, len(req.Prizes)) for i, pz := range req.Prizes { pzs[i] = active2023mod.Prize{ UID: int64(userInfo.UID), Type: int64(pz.Type), Name: pz.Name, Unit: int64(pz.Price), CreatedAt: now, UpdatedAt: now, } switch pz.Type { case active2023mod.PrizeTypeGold: // 金币 if pz.Price <= 0 { return errors.New("金币数量设置错误") } totalGold += pz.Price transactionType := txnmod.Active2023Reward wallet.Amount += int64(pz.Price) transactionLogs = append(transactionLogs, txnmod.TransactionLog{ TransNo: primitive.NewObjectID(), UID: userInfo.UID, Amount: int64(pz.Price), ActualAmount: float64(pz.Price), TranType: transactionType.Key(), TranTypeInt: int64(transactionType), Desc: fmt.Sprintf("%s-金币[%d个]", transactionType.Key(), pz.Price), RealAmount: walletmod.GetRealAmount(wallet), }) case active2023mod.PrizeTypeGameGold: // 游戏币 return errors.New("瓦力游戏已下架") case active2023mod.PrizeTypeProduct: // 现有产品 if pz.Param1 == "" { return errors.New("产品id为空") } productID, err := primitive.ObjectIDFromHex(pz.Param1) if err != nil { return errors.New("无效的产品id") } p, err := productmod.FindProduct(productID, "") if err != nil { return err } if p == nil { return errors.New("产品不存在") } pzs[i].ProductID = &productID switch p.ProductType { case commod.VideoDiscount: // 视频折扣卡 videoDiscounts = append(videoDiscounts, *p) case commod.VIP: // 会员卡 vipCards = append(vipCards, *p) default: return errors.New("不支持的产品类型") } case active2023mod.PrizeTypeVIPDate: // 会员日期 if pz.Price <= 0 { return errors.New("vip天数设置错误") } transactionType := txnmod.Active2023Reward transactionLogs = append(transactionLogs, txnmod.TransactionLog{ UID: userInfo.UID, Amount: 0, ActualAmount: 0, TranType: transactionType.Key(), TranTypeInt: int64(transactionType), TransNo: primitive.NilObjectID, Desc: fmt.Sprintf("%s获得VIP天数%d天", desc, pz.Price), DiscDoc: userInfo.DiscDoc, SysType: userInfo.SysType, RealAmount: walletmod.GetRealAmount(wallet), }) totalVipDates += pz.Price case active2023mod.PrizeTypeGoldCoinBonus: // 金币加赠券 if pz.Price <= 0 { return errors.New("金币加赠券面值设置错误") } expired := now.Add(time.Hour * 24 * 7) // 默认一周过期 if pz.ValidDate > 0 { expired = now.Add(time.Hour * 24 * time.Duration(pz.ValidDate)) } goldExtras = append(goldExtras, goldextramod.GoldExtra{ Uid: userInfo.UID, Name: "金币加赠券", Amount: pz.Price, Expired: expired, Used: false, Desc: "充值加送金币", Source: desc, CreatedAt: now, UpdatedAt: now, }) case active2023mod.PrizeTypeGoldWatch: // 金币观影券 if pz.Price <= 0 { return errors.New("会员观影券面值设置错误") } videoCoupons = append(videoCoupons, videocoupon.UserGoldVideoCoupon{ UID: userInfo.UID, Num: int(pz.Price), Source: videocoupon.GoldVideoCouponSource(desc), Used: false, CreatedAt: now, UpdatedAt: now, }) case active2023mod.PrizeTypeInKind: // 实物奖励 default: return fmt.Errorf("不支持的奖品类型: %d", pz.Type) } } needRefreshUserCache := false if totalGold > 0 { if wallet, err = walletmod.CreditAmount(t, int64(totalGold), userInfo.UID); err != nil { return err } } if totalVipDates > 0 { vipLevel := 1 // 默认vip等级为1 start := time.Now() discount := userInfo.PayVidDiscount if userInfo.VipExpireDate.After(start) { // 如果用户VIP未过期, 则VIP等级和视频折扣等级需要参考用户现在的情况 start = userInfo.VipExpireDate if userInfo.VipLevel > vipLevel { vipLevel = userInfo.VipLevel } } vipExpireDate := start.Add(time.Hour * 24 * time.Duration(totalVipDates)) sel := usermod.UserSelector{VipExpireDate: &vipExpireDate, VipLevel: &vipLevel, PayVidDiscount: &discount} if err := usermod.UpdateVIP(t, userInfo.UID, userInfo.VipExpireDate, sel); err != nil { return err } userInfo.VipExpireDate = vipExpireDate userInfo.VipLevel = vipLevel needRefreshUserCache = true } if len(videoCoupons) > 0 { if err = videocoupon.InsertManyTrans(t, videoCoupons); err != nil { return err } } if len(transactionLogs) > 0 { if err = txnmod.InsertManyTransactionLog(t, transactionLogs); err != nil { return err } } if len(goldExtras) > 0 { if err = goldextramod.InsertGoldExtras(t, &goldExtras); err != nil { return err } } if len(videoDiscounts) > 0 { if err = dealVideoDiscardCards(t, userInfo, videoDiscounts, desc); err != nil { return err } needRefreshUserCache = true } if len(vipCards) > 0 { if err = dealVipCards(t, userInfo, vipCards, desc); err != nil { return err } needRefreshUserCache = true } ids, err := active2023mod.InsertUserPrizes(t, &pzs) if err != nil { return err } if err := active2023mod.InsertUserLottery(t, &active2023mod.UserLottery{ UID: int64(userInfo.UID), Count: int64(req.Count), UseGold: int64(req.Gold), Prizes: ids, Desc: desc, CreatedAt: now, UpdatedAt: now, }); err != nil { return err } if needRefreshUserCache { go func() { _, _ = usermod.RefreshCacheAndGetUser(userInfo.UID) }() } return nil }) } func dealVideoDiscardCards(t *db.MongoTool, userInfo *usermod.User, products []productmod.Product, desc string) error { productIDsLen := len(products) if productIDsLen == 0 { return nil } videoDiscountLog, err := videodiscountmod.GetByUID(userInfo.UID) if err != nil { return err } // 最终过期时间 var videoDiscountTotal int // 最终折扣 var totalDates int // 总增加天数 // 所有视频折扣卡都加入日志 // 计算出最后用户的折扣和过期时间, 只更新一次用户配置 if videoDiscountLog != (videodiscountmod.VideoDiscountLog{}) { // 当存在用户配置时, 需要跟用户配置做对比 if videoDiscountLog.VideoDiscount > 0 && videoDiscountLog.VideoDiscount < 10 { // 如果用户有有效现有折扣 videoDiscountTotal = videoDiscountLog.VideoDiscount // 默认折扣取用户折扣 } } productHistorys := make([]prdcthsomod.ProductHistory, productIDsLen) transactionLogs := make([]txnmod.TransactionLog, productIDsLen) for i, p := range products { // 查找视频折扣卡详情 if p.Duration <= 0 || p.VideoDiscount >= 10 || p.VideoDiscount <= 0 { return errors.New("视频折扣卡配置不正确") } // 添加记录 historyID := primitive.NewObjectID() productHistorys[i] = prdcthsomod.ProductHistory{ ID: historyID, UID: userInfo.UID, ProductID: p.ID, Name: p.Name, ProductType: commod.VideoDiscount, DiscDoc: userInfo.DiscDoc, SysType: userInfo.SysType, ProductSnapShot: &p, } transactionLogs[i] = txnmod.TransactionLog{ UID: userInfo.UID, Amount: -p.DiscountedPrice, ActualAmount: 0, TranType: txnmod.VideoDiscount.Key(), TranTypeInt: int64(txnmod.VideoDiscount), TransNo: historyID, Desc: desc + "获得-" + p.Name, DiscDoc: userInfo.DiscDoc, SysType: userInfo.SysType, } if p.Duration <= 0 { p.Duration = 7 // 如果没有设置有效过期时间, 则默认为7天 } totalDates += p.Duration videoDiscount := p.VideoDiscount // 默认折扣: 产品折扣 // 最终取折扣值比较小的(折扣值越小说明享受的折扣越大) if videoDiscountTotal == 0 { videoDiscountTotal = videoDiscount } else if videoDiscountTotal > videoDiscount { videoDiscountTotal = videoDiscount } } // 写入历史纪录 if err = prdcthsomod.InsertManyProductHistory(t, productHistorys); err != nil { return err } if err = txnmod.InsertManyTransactionLog(t, transactionLogs); err != nil { return err } now := time.Now() start := now if videoDiscountLog != (videodiscountmod.VideoDiscountLog{}) { // 当存在用户配置时, 需要跟用户配置做对比 if videoDiscountLog.Expiration.After(now) { // 如果用户现有折扣未过期, 则过期时间从用户现有折扣过期时间算起去加折扣卡有效期 start = videoDiscountLog.Expiration } } expireAt := start.Add(time.Hour * 24 * time.Duration(totalDates)) // 修改用户折扣 set := videodiscountmod.EditSelector{ UID: &userInfo.UID, Expiration: &expireAt, VideoDiscount: &videoDiscountTotal, } return videodiscountmod.Upsert(t, &set) } // 抽奖中的vip卡只计算等级, 天数和视频折扣三种权益 // 不计算vip卡配置中附赠的金币和游戏币 func dealVipCards(t *db.MongoTool, userInfo *usermod.User, products []productmod.Product, desc string) error { productIDsLen := len(products) if productIDsLen == 0 { return nil } productHistorys := make([]prdcthsomod.ProductHistory, productIDsLen) transactionLogs := make([]txnmod.TransactionLog, productIDsLen) totalDuration := 0 // 所有VIP卡增加的天数总和 lowestDiscount := 10 // 所有VIP卡最大打折, 10表示原价不打折 hightestVipLevel := 1 // 最高VIP等级 for i, p := range products { if p.PayVidDiscount <= 0 || p.PayVidDiscount > 10 { return errors.New("VIP卡配置不正确") } if lowestDiscount > p.PayVidDiscount { lowestDiscount = p.PayVidDiscount } if hightestVipLevel < p.VipLevel { hightestVipLevel = p.VipLevel } totalDuration += p.Duration // 添加记录 historyID := primitive.NewObjectID() productHistorys[i] = prdcthsomod.ProductHistory{ ID: historyID, UID: userInfo.UID, ProductID: p.ID, Name: p.Name, ProductType: commod.VIP, DiscDoc: userInfo.DiscDoc, SysType: userInfo.SysType, ProductSnapShot: &p, } transactionLogs[i] = txnmod.TransactionLog{ UID: userInfo.UID, Amount: -p.DiscountedPrice, ActualAmount: 0, TranType: txnmod.Active2023VIP.Key(), TranTypeInt: int64(txnmod.Active2023VIP), TransNo: historyID, Desc: desc + "获得-" + p.Name, DiscDoc: userInfo.DiscDoc, SysType: userInfo.SysType, } } // 写入历史纪录 if err := prdcthsomod.InsertManyProductHistory(t, productHistorys); err != nil { return err } if err := txnmod.InsertManyTransactionLog(t, transactionLogs); err != nil { return err } start := time.Now() if userInfo.VipExpireDate.After(start) { // 如果用户VIP未过期, 则VIP等级和视频折扣等级需要参考用户现在的情况 start = userInfo.VipExpireDate } vipExpireDate := start.Add(time.Hour * 24 * time.Duration(totalDuration)) if userInfo.VipLevel > hightestVipLevel { hightestVipLevel = userInfo.VipLevel } if (userInfo.PayVidDiscount > 0 && userInfo.PayVidDiscount < 10) && userInfo.PayVidDiscount < lowestDiscount { lowestDiscount = userInfo.PayVidDiscount } sel := usermod.UserSelector{VipExpireDate: &vipExpireDate, VipLevel: &hightestVipLevel, PayVidDiscount: &lowestDiscount} if err := usermod.UpdateVIP(t, userInfo.UID, userInfo.VipExpireDate, sel); err != nil { return err } userInfo.VipExpireDate = vipExpireDate userInfo.VipLevel = hightestVipLevel userInfo.PayVidDiscount = lowestDiscount return nil }