From e3cc4705d18330f7ee238ecd2dae9eac65bfc06c Mon Sep 17 00:00:00 2001 From: chenlin Date: Thu, 14 Aug 2025 11:43:14 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E4=BC=98=E5=8C=96=E7=A7=AF=E5=88=86?= =?UTF-8?q?=E6=8A=BD=E5=A5=96=E6=8E=A5=E5=8F=A3=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controller/lottery.go | 198 +++++++++++++++++++++++++++++------------- model/game_card.go | 7 ++ model/lottery.go | 195 ++++++++++++++++++++++++++++++----------- model/redeem_code.go | 2 +- model/user.go | 9 ++ 5 files changed, 299 insertions(+), 112 deletions(-) diff --git a/controller/lottery.go b/controller/lottery.go index 6e8e088..3c1489c 100644 --- a/controller/lottery.go +++ b/controller/lottery.go @@ -1,11 +1,11 @@ package controller import ( - "errors" "fmt" "github.com/codinl/go-logger" "github.com/gin-gonic/gin" "github.com/jinzhu/gorm" + "github.com/pkg/errors" "mh-server/lib/auth" "mh-server/lib/status" "mh-server/lib/utils" @@ -27,6 +27,7 @@ func LotteryDraw(c *gin.Context) { RespJson(c, status.Unauthorized, nil) return } + logger.Info("参与抽奖用户uid:", uc.Uid) lotteryConfig, _ := model.GetLotteryConfig() if !lotteryConfig.LotteryEnabled { @@ -39,19 +40,23 @@ func LotteryDraw(c *gin.Context) { // 1. 判断用户当前积分是否足够 var userVm model.UserVm - if err := model.DB.Where("uid = ?", uc.Uid).First(&userVm).Error; err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) || userVm.Vm < uint32(lotteryConfig.CostPerDraw) { - RespJson(c, 400, "积分不足,无法抽奖") - } else { - RespJson(c, 500, "查询积分失败") - } + err := model.DB.Where("uid = ?", uc.Uid).First(&userVm).Error + if err != nil { + logger.Errorf("查询用户积分失败 uid=%d err=%v", uc.Uid, err) + RespJson(c, 400, "积分不足,无法抽奖") + return + } + if userVm.Vm < uint32(lotteryConfig.CostPerDraw) { + logger.Warnf("积分不足 uid=%d 积分=%d", uc.Uid, userVm.Vm) + RespJson(c, 400, "积分不足,无法抽奖") return } // 2. 获取可用奖品 var prizes []model.LotteryPrize if err := model.DB.Where("status = ? AND stock > 0", model.LotteryPrizeStatusEnabled).Find(&prizes).Error; err != nil { - RespJson(c, 500, "读取奖品失败") + logger.Error("读取奖品失败:", err) + RespJson(c, 500, "奖品更新中,请稍后再试...") return } if len(prizes) == 0 { @@ -65,8 +70,9 @@ func LotteryDraw(c *gin.Context) { } // 3. 扣除积分 - err := model.UserVmUpdateTx(model.DB, uc.Uid, -int(lotteryConfig.CostPerDraw), "lottery", "抽奖消耗积分") + err = model.UserVmUpdateTx(model.DB, uc.Uid, -int(lotteryConfig.CostPerDraw), "lottery", "抽奖消耗积分") if err != nil { + logger.Errorf("积分扣除失败 uid=%d err=%v", uc.Uid, err) RespJson(c, 400, "积分扣除失败") return } @@ -77,67 +83,91 @@ func LotteryDraw(c *gin.Context) { if r := recover(); r != nil { tx.Rollback() RespJson(c, 500, "系统异常") + return } }() - // 5. 查询抽奖次数 + // 5. 查询抽奖次数 & 更新总抽奖数 drawNumber, totalDrawNumber, err := model.GetLotteryDrawStats(uc.Uid) if err != nil { drawNumber = 0 totalDrawNumber = 0 } + logger.Infof("用户抽奖次数为:%d,总抽奖次数为:%d", drawNumber, totalDrawNumber) - // 更新总抽奖数 - if err = model.IncrementTotalDraw(); err != nil { - model.UserVmUpdateTx(model.DB, uc.Uid, int(lotteryConfig.CostPerDraw), "lottery", "抽奖失败积分补偿") - logger.Error("更新总抽奖次数失败:", err) - RespJson(c, 500, "抽奖失败,系统异常") + if err = model.IncrementTotalDraw(tx); err != nil { + tx.Rollback() + logger.Errorf("更新总抽奖次数失败 uid=%d err=%v", uc.Uid, err) + respNoPrize(c) return } - // 6. 白名单判断 + // 6. 获取用户信息 + userInfo := model.GetUserByUidTx(tx, uc.Uid) + if userInfo == nil { + tx.Rollback() + logger.Error("查询用户信息异常,uid:", uc.Uid) + respNoPrize(c) + return + } + + // 7. 白名单判断 var prize model.LotteryPrize var whiteEntry model.LotteryWhiteList - err = model.DB. - Where("uid = ?", uc.Uid).First(&whiteEntry).Error - - if err == nil { - // 命中白名单,直接返回配置奖品 - err = model.DB.First(&prize, whiteEntry.PrizeID).Error - if err != nil { - RespOK(c, model.LotteryDrawResponse{ - PrizeID: 0, - PrizeName: "谢谢参与", - PrizeLevel: 0, - Message: "很遗憾未中奖,祝你下次好运!", - }) + err = tx.Where("uid = ?", uc.Uid).First(&whiteEntry).Error + if errors.Is(err, gorm.ErrRecordNotFound) || (err == nil && drawNumber < int(whiteEntry.DrawNumber)) { // 普通抽奖 + if err == nil && drawNumber < int(whiteEntry.DrawNumber) { + logger.Errorf("用户%d在白名单,但抽奖次数 %d 小于设定次数 %d", uc.Uid, drawNumber, whiteEntry.DrawNumber) } - } else { + // 查询用户当天消费金额 + userTotalAmount := model.GetUserDailyTotalAmount(uc.Uid) + + isRentalMember := false + // 判断用户是否为租卡会员 + if userInfo.IsMemberNew() { + isRentalMember = true + } + + // 当用户抽奖次数超过40,则判断是否中过1-4等奖;收集用户已中奖的奖项 + hasWon4OrAbove, userPrizeIds, _ := model.PrepareUserLotteryInfo(uc.Uid) + // 普通抽奖逻辑 - prize, err = model.DrawFromDatabasePrizes(prizes, drawNumber, totalDrawNumber) + prize, err = model.DrawFromDatabasePrizes(prizes, drawNumber, totalDrawNumber, userTotalAmount, userPrizeIds, + hasWon4OrAbove, isRentalMember) if err != nil { - RespOK(c, model.LotteryDrawResponse{ - PrizeID: 0, - PrizeName: "谢谢参与", - PrizeLevel: 0, - Message: "很遗憾未中奖,祝你下次好运!", - }) + tx.Rollback() + logger.Errorf("DrawFromDatabasePrizes err uid=%d err=%v", uc.Uid, err) + respNoPrize(c) + return + } + } else if err != nil { + logger.Errorf("查询白名单失败 uid=%d err=%v", uc.Uid, err) + RespJson(c, 500, "系统异常,请稍后再试...") + return + } else { + logger.Infof("用户%d在白名单中,%d等奖,第%d次中奖", uc.Uid, whiteEntry.PrizeLevel, whiteEntry.DrawNumber) + // 命中白名单,直接返回配置奖品 + err = tx.Where("level = ? and stock > 1", whiteEntry.PrizeLevel).First(&prize).Error + if err != nil { + tx.Rollback() + logger.Errorf("白名单抽奖失败 uid=%d err=%v", uc.Uid, err) + respNoPrize(c) return } } - // 7. 减少奖品库存(乐观锁) + // 8. 减少奖品库存(乐观锁) result := tx.Model(&model.LotteryPrize{}). Where("id = ? AND stock > 0", prize.ID). UpdateColumn("stock", gorm.Expr("stock - 1")) if result.Error != nil || result.RowsAffected == 0 { tx.Rollback() - model.UserVmUpdateTx(model.DB, uc.Uid, int(lotteryConfig.CostPerDraw), "lottery", "抽奖失败积分补偿") - RespJson(c, 500, "奖品库存不足") + logger.Errorf("减少奖品库存失败 uid=%d err=%v", uc.Uid, result.Error) + respNoPrize(c) return } - // 8. 记录中奖信息 + // 9. 记录中奖信息 isWin := prize.PrizeType != model.LotteryPrizeTypeNone lotteryRecord := model.LotteryRecord{ Uid: uint(uc.Uid), @@ -154,34 +184,63 @@ func LotteryDraw(c *gin.Context) { err = tx.Create(&lotteryRecord).Error if err != nil { tx.Rollback() - model.UserVmUpdateTx(model.DB, uc.Uid, int(lotteryConfig.CostPerDraw), "lottery", "抽奖失败积分补偿") - RespJson(c, 500, "记录中奖信息失败") + logger.Errorf("记录中奖信息失败 uid=%d err=%v", uc.Uid, err) + respNoPrize(c) return } - // 如果是积分奖品,则直接发放积分 - if prize.PrizeType == model.LotteryPrizeTypePoint { - err = model.UserVmUpdateTx(tx, uc.Uid, prize.PrizeValue, "lottery_add", "抽奖赢取积分") + // 10. 发放奖品 如果是租卡会员兑换码,则直接发放兑换券 + if prize.PrizeType == model.LotteryPrizeTypeRentCard30 { + var redeemCode model.RedeemCode + err = tx.Table("redeem_code").Where("status=?", model.RedeemCodeStatusStock). + Where("code_type=?", model.CodeTypeMemberCard30).Order("id ASC").Limit(1).Find(&redeemCode).Error if err != nil { - model.UserVmUpdateTx(model.DB, uc.Uid, int(lotteryConfig.CostPerDraw), "lottery", "抽奖失败积分补偿") tx.Rollback() - RespJson(c, 400, "积分发放失败") + logger.Error("get redeem_code err:", err) + respNoPrize(c) return } - } - // 如果是优惠券奖品,则直接发放优惠券 - if prize.PrizeType == model.LotteryPrizeTypeCoupon { - var coupon model.Coupon - err = model.NewCouponQuerySet(model.DB).ActivityIdEq(uint32(prize.PrizeValue)).One(&coupon) + userRedeemCode := &model.UserRedeemCode{ + Uid: uc.Uid, + Status: model.UserRedeemCodeStatusHold, + SerialCode: redeemCode.SerialCode, + CodeType: redeemCode.CodeType, + ActivityType: model.RedeemCodeActivityTypeLottery, + StoreId: uint32(userInfo.StoreId), + } + err = tx.Create(userRedeemCode).Error if err != nil { + tx.Rollback() + logger.Error("create userRedeemCode err:", err) + respNoPrize(c) + return + } + } else if prize.PrizeType == model.LotteryPrizeTypePoint { // 如果是积分奖品,则直接发放积分 + err = model.UserVmUpdateTx(tx, uc.Uid, prize.PrizeValue, "lottery_add", "抽奖赢取积分") + if err != nil { + tx.Rollback() + logger.Error("积分发放失败:", err) + respNoPrize(c) + return + } + } else if prize.PrizeType == model.LotteryPrizeTypeCoupon { // 如果是优惠券奖品,则直接发放优惠券 + var coupon model.Coupon + err = model.NewCouponQuerySet(tx).ActivityIdEq(uint32(prize.PrizeValue)).One(&coupon) + if err != nil { + tx.Rollback() logger.Error("get coupon err:", err) + respNoPrize(c) + return } if coupon.ID != 0 { couponCode, err := utils.GenerateRandomNumber19() if err != nil { + tx.Rollback() logger.Error("GenerateRandomNumber19err:", err) + respNoPrize(c) + return } userCoupon := &model.UserCoupon{ Uid: uc.Uid, @@ -205,26 +264,26 @@ func LotteryDraw(c *gin.Context) { err = tx.Create(userCoupon).Error if err != nil { - logger.Error("user coupon err:", err) - model.UserVmUpdateTx(model.DB, uc.Uid, int(lotteryConfig.CostPerDraw), "lottery", "抽奖失败积分补偿") tx.Rollback() - RespJson(c, 400, "优惠券发放失败") + logger.Error("优惠券发放失败:", err) + respNoPrize(c) return } } } - // 9. 提交事务 - if err := tx.Commit().Error; err != nil { - model.UserVmUpdateTx(model.DB, uc.Uid, int(lotteryConfig.CostPerDraw), "lottery", "抽奖失败积分补偿") - RespJson(c, 500, "抽奖失败,请稍后重试") + // 11. 提交事务 + if err = tx.Commit().Error; err != nil { + tx.Rollback() + logger.Errorf("抽奖提交事务失败 uid=%d err=%v", uc.Uid, err) + respNoPrize(c) return } - // 10. 返回中奖结果 + // 12. 返回中奖结果 var message string if prize.Name == "谢谢参与" || prize.PrizeType == model.LotteryPrizeTypeNone { - message = "很遗憾未中奖,祝你下次好运~" + message = "很遗憾未中奖,祝您下次好运~" } else { message = fmt.Sprintf("🎉 恭喜您获得【%s】", prize.Name) } @@ -235,7 +294,15 @@ func LotteryDraw(c *gin.Context) { PrizeLevel: prize.Level, Message: message, }) +} +func respNoPrize(c *gin.Context) { + RespOK(c, model.LotteryDrawResponse{ + PrizeID: 8, + PrizeName: "谢谢参与", + PrizeLevel: 0, + Message: "很遗憾未中奖,祝您下次好运!", + }) } // LotteryPrizes 奖品列表接口 @@ -333,6 +400,13 @@ func LotteryRecords(c *gin.Context) { db = db.Where("r.is_win = ?", false) } + if req.PrizeLevel != 0 { + db = db.Where("r.prize_level = ?", req.PrizeLevel) + } + if req.PrizeType != 0 { + db = db.Where("r.prize_type = ?", req.PrizeType) + } + // 获取总数 var total int64 if err := db.Count(&total).Error; err != nil { diff --git a/model/game_card.go b/model/game_card.go index 51e627e..522a25d 100644 --- a/model/game_card.go +++ b/model/game_card.go @@ -85,6 +85,13 @@ func (m *User) IsMember() bool { return false } +func (m *User) IsMemberNew() bool { + if m.MemberLevel == 2 || m.MemberLevel == 3 || m.MemberLevel == 4 || m.MemberLevel == 5 { + return true + } + return false +} + //var GameCardListUpdateTime = time.Time{} func GetGameCardList(sortType, page, pageSize int, gameTypeIds []uint64, storeId, uid uint32) ([]GameCard, uint32, error) { diff --git a/model/lottery.go b/model/lottery.go index 6ad87b8..f3b78ff 100644 --- a/model/lottery.go +++ b/model/lottery.go @@ -2,14 +2,17 @@ package model import ( "errors" + "github.com/codinl/go-logger" + "github.com/jinzhu/gorm" "math/rand" "time" ) const ( - LotteryPrizeTypePoint = 1 // 积分奖品 - LotteryPrizeTypeCoupon = 2 // 优惠券奖品 - LotteryPrizeTypePhysical = 3 // 实物奖品 + LotteryPrizeTypePoint = 1 // 积分奖品 + LotteryPrizeTypeCoupon = 2 // 优惠券奖品 + LotteryPrizeTypePhysical = 3 // 实物奖品 + LotteryPrizeTypeRentCard30 = 4 // 30天租卡会员兑换码 LotteryRecordStatusPending = 0 // 待处理 LotteryRecordStatusDelivered = 1 // 已发放 @@ -20,22 +23,26 @@ const ( LotteryPrizeStatusDisabled = 2 // 禁用奖品 LotteryPrizeTypeNone = 0 // 谢谢参与 + + LotteryCheckLimit = 40 // 抽奖超过40次时判断是否有中过1-4等奖 + LotteryDefaultLevel = 4 // 抽奖超过40次时且未中过1-4等奖,则默认中的奖项 ) // LotteryPrize 奖品信息表(抽奖) type LotteryPrize struct { Model - Name string `json:"name"` // 奖品名称 - PrizeType int `json:"prize_type"` // 奖品类型:1-积分 2-优惠券 3-实物 0-谢谢参与 - PrizeValue int `json:"prize_value"` // 奖品值(积分数或券ID等) - Level int `json:"level"` // 奖品等级,如1等奖、2等奖等 - Weight int `json:"weight"` // 抽奖权重 - Stock int `json:"stock"` // 剩余库存 - Status int `json:"status"` // 奖品状态:1-启用 2-禁用 - UnlockUserCount int `json:"unlock_user_count"` // 解锁条件:用户个人抽奖次数 - UnlockTotalCount int `json:"unlock_total_count"` // 解锁条件:所有用户总抽奖次数 - Images string `json:"images"` // 奖品图片 + Name string `json:"name"` // 奖品名称 + PrizeType int `json:"prize_type"` // 奖品类型:1-积分 2-优惠券 3-实物 4-30天租卡会员兑换码 0-谢谢参与 + PrizeValue int `json:"prize_value"` // 奖品值(积分数或券ID等) + Level int `json:"level"` // 奖品等级,如1等奖、2等奖等 + Weight int `json:"weight"` // 抽奖权重 + Stock int `json:"stock"` // 剩余库存 + Status int `json:"status"` // 奖品状态:1-启用 2-禁用 + UnlockUserCount int `json:"unlock_user_count"` // 解锁条件:用户个人抽奖次数 + UnlockTotalCount int `json:"unlock_total_count"` // 解锁条件:所有用户总抽奖次数 + DailyAmountLimit float64 `json:"daily_amount_limit"` // 当天消费金额限制(0 表示不限制) + Images string `json:"images"` // 奖品图片 } // LotteryRecord 抽奖记录 @@ -69,10 +76,12 @@ type LotteryPrizeOrder struct { ReceiverAddr string `json:"receiver_addr"` // 收件地址 // 发货信息 - LogisticsCompany string `json:"logistics_company"` // 快递公司 - LogisticsNumber string `json:"logistics_number"` // 快递单号 - ShippedAt time.Time `json:"shipped_at"` // 发货时间 - ReceivedAt time.Time `json:"received_at"` // 收货时间 + LogisticsCompany string `json:"logistics_company"` // 快递公司 + LogisticsNumber string `json:"logistics_number"` // 快递单号 + DeliverShopperCode uint32 `json:"deliver_shopper_code"` // 发货店员用户ID + DeliverShopperName string `json:"deliver_shopper_name"` // 发货店员名称 + ShippedAt *time.Time `json:"shipped_at"` // 发货时间 + ReceivedAt *time.Time `json:"received_at"` // 收货时间 Status int `json:"status"` // 发货状态:0-待发货 1-已发货 2-已收货 3-取消 } @@ -90,7 +99,7 @@ type LotteryWhiteList struct { Uid uint `json:"uid"` // 用户ID DrawNumber uint `json:"draw_number"` // 第几次抽奖命中(如第3次) - PrizeID uint `json:"prize_id"` // 奖品ID(外键指向 LotteryPrize) + PrizeLevel uint `json:"prize_level"` // 奖品等级 } type LotteryDrawResponse struct { @@ -112,9 +121,11 @@ type LotteryPrizePageResponse struct { } type LotteryRecordQuery struct { - PageNum int `json:"page_num"` - PageSize int `json:"page_size"` - WinStatus int `json:"win_status"` // 新增字段:0=全部,1=已中奖,2=未中奖 + PageNum int `json:"page_num"` + PageSize int `json:"page_size"` + WinStatus int `json:"win_status"` // 新增字段:0=全部,1=已中奖,2=未中奖 + PrizeLevel int `json:"prize_level"` // 奖品等级 + PrizeType int `json:"prize_type"` // 奖品类型 1-积分 2-优惠券 3-实物 4-30天租卡会员兑换码 } type LotteryRecordPageResponse struct { @@ -123,52 +134,91 @@ type LotteryRecordPageResponse struct { List []LotteryRecord `json:"list"` // 抽奖记录列表 } -func DrawFromDatabasePrizes(prizes []LotteryPrize, userDrawCount, globalDrawCount int) (LotteryPrize, error) { - var available []LotteryPrize - totalWeight := 0 +func DrawFromDatabasePrizes(prizes []LotteryPrize, userDrawCount, globalDrawCount int, totalAmount float64, + userPrizeIds map[uint32]bool, hasWon4OrAbove bool, isRentalMember bool) (LotteryPrize, error) { + // 1. 判断是否超过40次且未中过4等奖以上,强制发4等奖 + if userDrawCount > LotteryCheckLimit && !hasWon4OrAbove { + for _, p := range prizes { + // 假设奖品等级字段为 PrizeLevel,4等奖等级为4 + if p.Level == LotteryDefaultLevel && p.Stock > 0 { + logger.Infof("用户抽奖40次且未中过4等奖以上,强制发4等奖") + return p, nil + } + } + return LotteryPrize{}, errors.New("未找到4等奖奖品") + } + // 2. 筛选可抽奖品 + available := make([]LotteryPrize, 0) + totalWeight := 0 for _, p := range prizes { - // 同时满足两个解锁条件才可参与抽奖 - if userDrawCount >= p.UnlockUserCount && globalDrawCount >= p.UnlockTotalCount { + // 权重无效直接跳过 + if p.Weight <= 0 || p.Stock <= 0 { + continue + } + // 用户是租卡会员则不能抽30日租卡会员奖项 + if p.PrizeType == LotteryPrizeTypeRentCard30 && isRentalMember { + logger.Infof("用户已是租卡会员,不再抽取30天租卡会员兑换码") + continue + } + + if p.DailyAmountLimit != 0 && totalAmount > p.DailyAmountLimit { + logger.Infof("用户当天有消费,且满足消费金额,可以抽取奖品:%s", p.Name) + // 超过当天消费限制,直接加入,不限制次数 available = append(available, p) totalWeight += p.Weight + continue + } else { + // 同时满足两个解锁条件才可参与抽奖 + if userDrawCount >= p.UnlockUserCount && globalDrawCount >= p.UnlockTotalCount { + logger.Infof("用户抽奖次数及总抽奖次数满足要求,可以抽取奖品:%s", p.Name) + available = append(available, p) + totalWeight += p.Weight + } } } - if totalWeight == 0 { + if len(available) == 0 || totalWeight <= 0 { return LotteryPrize{}, errors.New("无可抽奖奖品") } + logger.Infof("此次抽奖总权重:%d", totalWeight) - r := rand.Intn(totalWeight) + // 3. 随机抽奖 + r := rand.New(rand.NewSource(time.Now().UnixNano())).Intn(totalWeight) + logger.Infof("随机数是:%d", r) acc := 0 - for _, p := range available { - acc += p.Weight + var selected *LotteryPrize + for i := range available { + acc += available[i].Weight if r < acc { - return p, nil + selected = &available[i] + logger.Infof("抽中的奖品是:%s", available[i].Name) + break } } - - return LotteryPrize{}, errors.New("抽奖失败") -} - -func getUnlockDrawCount(level int) int { - switch level { - case 1: - return 5000 - case 2: - return 2000 - case 3: - return 1000 - case 4: - return 500 - default: - return 0 + // 安全检查 + if selected == nil { + return LotteryPrize{}, errors.New("随机抽取失败,权重配置异常") } + + // 4. 处理重复中奖 同一用户不重复中某个奖项,如再次中奖则自动替换为20积分 + if userPrizeIds[selected.ID] { + for _, p := range prizes { + if p.PrizeType == LotteryPrizeTypePoint && p.Stock > 0 { + logger.Info("用户重复中奖,自动替换奖品。", "uid:", selected.ID, "替换为", p.Name) + return p, nil + } + } + // 如果没找到积分奖项,也要安全返回 + return LotteryPrize{}, errors.New("重复中奖且无积分奖项可替代") + } + + return *selected, nil } // IncrementTotalDraw 更新总抽奖数 -func IncrementTotalDraw() error { - return DB.Exec("UPDATE lottery_stats SET total_draw = total_draw + 1 WHERE id = 1").Error +func IncrementTotalDraw(tx *gorm.DB) error { + return tx.Exec("UPDATE lottery_stats SET total_draw = total_draw + 1 WHERE id = 1").Error } // GetLotteryDrawStats 查询某个用户的抽奖次数和全站总抽奖次数 @@ -235,3 +285,50 @@ type LotteryPrizeOrderDetailResponse struct { PrizeValue int `json:"prize_value"` // 奖品值 Images string `json:"images"` // 奖品图片 } + +// GetUserDailyTotalAmount 查询某个用户当天的消费总金额 +func GetUserDailyTotalAmount(uid uint32) (totalAmount float64) { + now := time.Now() + startTime := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) + endTime := startTime.Add(24 * time.Hour).Add(-time.Second) + + var result struct { + Amount float64 + } + + err := DB.Model(&ErpOrder{}). + Where("uid = ? AND created_at BETWEEN ? AND ?", uid, startTime, endTime). + Select("SUM(total_amount) as amount"). + Scan(&result).Error + if err != nil { + return 0 + } + + return result.Amount +} + +// PrepareUserLotteryInfo 传入用户ID,判断是否中过4等奖及以上,收集用户已中奖奖品ID +func PrepareUserLotteryInfo(uid uint32) (hasWon4OrAbove bool, userPrizeIds map[uint32]bool, err error) { + // 初始化map + userPrizeIds = make(map[uint32]bool) + + // 查询用户已中奖记录,且状态是中奖的(IsWin = true) + var records []LotteryRecord + err = DB.Model(&LotteryRecord{}). + Where("uid = ? AND is_win = ? AND prize_type != ?", uid, true, LotteryPrizeTypePoint). + Find(&records).Error + if err != nil { + return + } + + // 判断是否中过4等奖以上(奖品等级<=4,且中奖) + hasWon4OrAbove = false + for _, r := range records { + userPrizeIds[uint32(r.PrizeId)] = true + if r.PrizeLevel <= LotteryDefaultLevel { + hasWon4OrAbove = true + } + } + + return +} diff --git a/model/redeem_code.go b/model/redeem_code.go index a03d6ca..5c261f4 100644 --- a/model/redeem_code.go +++ b/model/redeem_code.go @@ -37,7 +37,7 @@ const ( const ( RedeemCodeActivityTypeStore = 1 // 门店赠送 RedeemCodeActivityTypeUserInvite = 2 // 用户邀请 - + RedeemCodeActivityTypeLottery = 3 // 积分抽奖 ) // gen:qs diff --git a/model/user.go b/model/user.go index b9e1af5..226d3b2 100644 --- a/model/user.go +++ b/model/user.go @@ -296,6 +296,15 @@ func GetUserByUid(uid uint32) *User { return user } +func GetUserByUidTx(tx *gorm.DB, uid uint32) *User { + user := new(User) + if err := NewUserQuerySet(tx).UidEq(uid).One(user); err != nil { + logger.Error(err, uid) + return user + } + return user +} + const StoreDateTimeFormat = "2006.01.02" // GetUserEffectiveStore 获取店员当前的有效门店(邀请客户时使用)