1、新增积分抽奖相关接口;
This commit is contained in:
parent
70750755d3
commit
8509413f9f
|
@ -228,6 +228,10 @@ func GameCardSearch(c *gin.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cardList == nil {
|
||||||
|
cardList = []model.GameCard{}
|
||||||
|
}
|
||||||
|
|
||||||
ret := map[string]interface{}{
|
ret := map[string]interface{}{
|
||||||
"card_list": cardList,
|
"card_list": cardList,
|
||||||
"cur_page": req.Page,
|
"cur_page": req.Page,
|
||||||
|
|
673
controller/lottery.go
Normal file
673
controller/lottery.go
Normal file
|
@ -0,0 +1,673 @@
|
||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/codinl/go-logger"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
"mh-server/lib/auth"
|
||||||
|
"mh-server/lib/status"
|
||||||
|
"mh-server/lib/utils"
|
||||||
|
"mh-server/model"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LotteryDraw 抽奖接口
|
||||||
|
// @Summary 抽奖接口
|
||||||
|
// @Tags 积分抽奖
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} model.LotteryDrawResponse
|
||||||
|
// @Router /api/v1/lottery/draw [post]
|
||||||
|
func LotteryDraw(c *gin.Context) {
|
||||||
|
uc := auth.GetCurrentUser(c)
|
||||||
|
if uc == nil {
|
||||||
|
RespJson(c, status.Unauthorized, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lotteryConfig, _ := model.GetLotteryConfig()
|
||||||
|
if !lotteryConfig.LotteryEnabled {
|
||||||
|
RespJson(c, 500, "抽奖系统维护中...")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if lotteryConfig.CostPerDraw == 0 { // 每次抽奖消耗的积分,可配置
|
||||||
|
lotteryConfig.CostPerDraw = 99
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, "查询积分失败")
|
||||||
|
}
|
||||||
|
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, "读取奖品失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(prizes) == 0 {
|
||||||
|
RespOK(c, model.LotteryDrawResponse{
|
||||||
|
PrizeID: 0,
|
||||||
|
PrizeName: "谢谢参与",
|
||||||
|
PrizeLevel: 0,
|
||||||
|
Message: "奖品暂不可用,稍后再试~",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 扣除积分
|
||||||
|
err := model.UserVmUpdateTx(model.DB, uc.Uid, -int(lotteryConfig.CostPerDraw), "lottery", "抽奖消耗积分")
|
||||||
|
if err != nil {
|
||||||
|
RespJson(c, 400, "积分扣除失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 开启事务处理:扣积分 + 抽奖 + 减库存 + 记录
|
||||||
|
tx := model.DB.Begin()
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
RespJson(c, 500, "系统异常")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 5. 查询抽奖次数
|
||||||
|
drawNumber, totalDrawNumber, err := model.GetLotteryDrawStats(uc.Uid)
|
||||||
|
if err != nil {
|
||||||
|
drawNumber = 0
|
||||||
|
totalDrawNumber = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新总抽奖数
|
||||||
|
if err = model.IncrementTotalDraw(); err != nil {
|
||||||
|
model.UserVmUpdateTx(model.DB, uc.Uid, int(lotteryConfig.CostPerDraw), "lottery", "抽奖失败积分补偿")
|
||||||
|
logger.Error("更新总抽奖次数失败:", err)
|
||||||
|
RespJson(c, 500, "抽奖失败,系统异常")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 白名单判断
|
||||||
|
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: "很遗憾未中奖,祝你下次好运!",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 普通抽奖逻辑
|
||||||
|
prize, err = model.DrawFromDatabasePrizes(prizes, drawNumber, totalDrawNumber)
|
||||||
|
if err != nil {
|
||||||
|
RespOK(c, model.LotteryDrawResponse{
|
||||||
|
PrizeID: 0,
|
||||||
|
PrizeName: "谢谢参与",
|
||||||
|
PrizeLevel: 0,
|
||||||
|
Message: "很遗憾未中奖,祝你下次好运!",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 减少奖品库存(乐观锁)
|
||||||
|
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, "奖品库存不足")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. 记录中奖信息
|
||||||
|
isWin := prize.PrizeType != model.LotteryPrizeTypeNone
|
||||||
|
lotteryRecord := model.LotteryRecord{
|
||||||
|
Uid: uint(uc.Uid),
|
||||||
|
PrizeId: uint(prize.ID),
|
||||||
|
PrizeName: prize.Name,
|
||||||
|
PrizeType: prize.PrizeType,
|
||||||
|
PrizeLevel: prize.Level,
|
||||||
|
Status: model.LotteryRecordStatusDelivered,
|
||||||
|
IsWin: isWin,
|
||||||
|
}
|
||||||
|
if prize.PrizeType == model.LotteryPrizeTypePhysical { // 判断是不是实物奖品
|
||||||
|
lotteryRecord.Status = model.LotteryRecordStatusPending // 待处理
|
||||||
|
}
|
||||||
|
err = tx.Create(&lotteryRecord).Error
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
model.UserVmUpdateTx(model.DB, uc.Uid, int(lotteryConfig.CostPerDraw), "lottery", "抽奖失败积分补偿")
|
||||||
|
RespJson(c, 500, "记录中奖信息失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是积分奖品,则直接发放积分
|
||||||
|
if prize.PrizeType == model.LotteryPrizeTypePoint {
|
||||||
|
err = model.UserVmUpdateTx(tx, uc.Uid, prize.PrizeValue, "lottery_add", "抽奖赢取积分")
|
||||||
|
if err != nil {
|
||||||
|
model.UserVmUpdateTx(model.DB, uc.Uid, int(lotteryConfig.CostPerDraw), "lottery", "抽奖失败积分补偿")
|
||||||
|
tx.Rollback()
|
||||||
|
RespJson(c, 400, "积分发放失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是优惠券奖品,则直接发放优惠券
|
||||||
|
if prize.PrizeType == model.LotteryPrizeTypeCoupon {
|
||||||
|
var coupon model.Coupon
|
||||||
|
err = model.NewCouponQuerySet(model.DB).ActivityIdEq(uint32(prize.PrizeValue)).One(&coupon)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("get coupon err:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if coupon.ID != 0 {
|
||||||
|
couponCode, err := utils.GenerateRandomNumber19()
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("GenerateRandomNumber19err:", err)
|
||||||
|
}
|
||||||
|
userCoupon := &model.UserCoupon{
|
||||||
|
Uid: uc.Uid,
|
||||||
|
CouponId: coupon.ID,
|
||||||
|
CouponType: coupon.CouponType,
|
||||||
|
ActivityType: coupon.ActivityType,
|
||||||
|
ActivityId: coupon.ActivityId,
|
||||||
|
Value: coupon.Value,
|
||||||
|
State: 1,
|
||||||
|
ActiveStart: time.Now(),
|
||||||
|
ActiveEnd: time.Now().AddDate(0, 1, 0),
|
||||||
|
UseTime: time.Time{},
|
||||||
|
MemberLevel: coupon.MemberLevel,
|
||||||
|
Approach: 0,
|
||||||
|
PromotionalSales: 0,
|
||||||
|
RedeemCode: "",
|
||||||
|
CategoryNumber: coupon.CategoryNumber,
|
||||||
|
CommodityNumber: coupon.CommodityNumber,
|
||||||
|
Code: couponCode,
|
||||||
|
}
|
||||||
|
|
||||||
|
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, "优惠券发放失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9. 提交事务
|
||||||
|
if err := tx.Commit().Error; err != nil {
|
||||||
|
model.UserVmUpdateTx(model.DB, uc.Uid, int(lotteryConfig.CostPerDraw), "lottery", "抽奖失败积分补偿")
|
||||||
|
RespJson(c, 500, "抽奖失败,请稍后重试")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10. 返回中奖结果
|
||||||
|
var message string
|
||||||
|
if prize.Name == "谢谢参与" || prize.PrizeType == model.LotteryPrizeTypeNone {
|
||||||
|
message = "很遗憾未中奖,祝你下次好运~"
|
||||||
|
} else {
|
||||||
|
message = fmt.Sprintf("🎉 恭喜您获得【%s】", prize.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
RespOK(c, model.LotteryDrawResponse{
|
||||||
|
PrizeID: int(prize.ID),
|
||||||
|
PrizeName: prize.Name,
|
||||||
|
PrizeLevel: prize.Level,
|
||||||
|
Message: message,
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// LotteryPrizes 奖品列表接口
|
||||||
|
// @Summary 奖品列表
|
||||||
|
// @Tags 积分抽奖
|
||||||
|
// @Produce json
|
||||||
|
// @Param data body model.LotteryPrizeQuery true "分页参数"
|
||||||
|
// @Success 200 {object} model.LotteryPrizePageResponse
|
||||||
|
// @Router /api/v1/lottery/prizes [post]
|
||||||
|
func LotteryPrizes(c *gin.Context) {
|
||||||
|
var req model.LotteryPrizeQuery
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
RespJson(c, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.PageNum <= 0 {
|
||||||
|
req.PageNum = 1
|
||||||
|
}
|
||||||
|
if req.PageSize <= 0 || req.PageSize > 100 {
|
||||||
|
req.PageSize = 20
|
||||||
|
}
|
||||||
|
offset := (req.PageNum - 1) * req.PageSize
|
||||||
|
|
||||||
|
var total int64
|
||||||
|
if err := model.DB.Model(&model.LotteryPrize{}).
|
||||||
|
Where("status = ?", model.LotteryPrizeStatusEnabled). // 仅启用奖品
|
||||||
|
Count(&total).Error; err != nil {
|
||||||
|
RespJson(c, 500, "获取奖品总数失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var prizes []model.LotteryPrize
|
||||||
|
err := model.DB.
|
||||||
|
Where("status = ?", model.LotteryPrizeStatusEnabled).
|
||||||
|
Order("level ASC").
|
||||||
|
Limit(req.PageSize).
|
||||||
|
Offset(offset).
|
||||||
|
Find(&prizes).Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
RespJson(c, 500, "获取奖品列表失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := model.LotteryPrizePageResponse{
|
||||||
|
Count: uint32(total),
|
||||||
|
PageNum: req.PageNum,
|
||||||
|
List: prizes,
|
||||||
|
}
|
||||||
|
|
||||||
|
RespOK(c, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LotteryRecords 抽奖记录接口(分页)
|
||||||
|
// @Summary 用户抽奖记录(分页)
|
||||||
|
// @Tags 积分抽奖
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param data body model.LotteryRecordQuery true "分页参数"
|
||||||
|
// @Success 200 {array} model.LotteryRecordPageResponse
|
||||||
|
// @Router /api/v1/lottery/records [post]
|
||||||
|
func LotteryRecords(c *gin.Context) {
|
||||||
|
uc := auth.GetCurrentUser(c)
|
||||||
|
if uc == nil {
|
||||||
|
RespJson(c, status.Unauthorized, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req model.LotteryRecordQuery
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
RespJson(c, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认分页参数
|
||||||
|
if req.PageNum <= 0 {
|
||||||
|
req.PageNum = 1
|
||||||
|
}
|
||||||
|
if req.PageSize <= 0 || req.PageSize > 100 {
|
||||||
|
req.PageSize = 20
|
||||||
|
}
|
||||||
|
offset := (req.PageNum - 1) * req.PageSize
|
||||||
|
|
||||||
|
// 构建基础查询
|
||||||
|
db := model.DB.Table("lottery_record AS r").
|
||||||
|
Joins("LEFT JOIN lottery_prize AS p ON r.prize_id = p.id").
|
||||||
|
Where("r.uid = ?", uc.Uid)
|
||||||
|
|
||||||
|
// 根据 win_status 筛选记录:0=全部,1=已中奖,2=未中奖
|
||||||
|
switch req.WinStatus {
|
||||||
|
case 1:
|
||||||
|
db = db.Where("r.is_win = ?", true)
|
||||||
|
case 2:
|
||||||
|
db = db.Where("r.is_win = ?", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取总数
|
||||||
|
var total int64
|
||||||
|
if err := db.Count(&total).Error; err != nil {
|
||||||
|
RespJson(c, 500, "获取记录总数失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询数据
|
||||||
|
type LotteryRecordWithImage struct {
|
||||||
|
model.LotteryRecord
|
||||||
|
PrizeImages string `json:"prize_images"`
|
||||||
|
}
|
||||||
|
var joinedRecords []LotteryRecordWithImage
|
||||||
|
|
||||||
|
if err := db.Select("r.*, p.images AS prize_images").
|
||||||
|
Order("r.created_at DESC").
|
||||||
|
Limit(req.PageSize).
|
||||||
|
Offset(offset).
|
||||||
|
Scan(&joinedRecords).Error; err != nil {
|
||||||
|
RespJson(c, 500, "获取抽奖记录失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 映射结果
|
||||||
|
var records []model.LotteryRecord
|
||||||
|
for _, jr := range joinedRecords {
|
||||||
|
record := jr.LotteryRecord
|
||||||
|
record.Images = jr.PrizeImages
|
||||||
|
records = append(records, record)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := model.LotteryRecordPageResponse{
|
||||||
|
Count: uint32(total),
|
||||||
|
PageNum: req.PageNum,
|
||||||
|
List: records,
|
||||||
|
}
|
||||||
|
|
||||||
|
RespOK(c, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecentWinners 查询最近中奖用户的抽奖记录
|
||||||
|
// @Summary 查询最近中奖用户
|
||||||
|
// @Tags 积分抽奖
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param data body model.RecentWinnersQuery true "查询参数(记录条数)"
|
||||||
|
// @Success 200 {object} model.RecentWinnersResponse
|
||||||
|
// @Router /api/v1/lottery/recent_winners [post]
|
||||||
|
func RecentWinners(c *gin.Context) {
|
||||||
|
var req model.RecentWinnersQuery
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil || req.Limit <= 0 {
|
||||||
|
req.Limit = 10 // 默认值
|
||||||
|
}
|
||||||
|
|
||||||
|
var total int64
|
||||||
|
if err := model.DB.Model(&model.LotteryRecord{}).
|
||||||
|
Where("is_win = ?", true).
|
||||||
|
Count(&total).Error; err != nil {
|
||||||
|
RespJson(c, 500, "获取中奖总数失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var records []model.LotteryRecord
|
||||||
|
if err := model.DB.
|
||||||
|
Where("is_win = ? AND prize_type != ?", true, model.LotteryPrizeTypeNone).
|
||||||
|
Order("created_at DESC").
|
||||||
|
Limit(req.Limit).
|
||||||
|
Find(&records).Error; err != nil {
|
||||||
|
RespJson(c, 500, "获取中奖记录失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := model.RecentWinnersResponse{
|
||||||
|
Count: uint32(total),
|
||||||
|
List: records,
|
||||||
|
}
|
||||||
|
RespOK(c, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublicLotteryConfigHandler 查询抽奖配置(公开接口)
|
||||||
|
// @Summary 查询抽奖模块配置(公开)
|
||||||
|
// @Tags 积分抽奖
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} model.LotteryConfig
|
||||||
|
// @Router /api/v1/lottery/config/public [post]
|
||||||
|
func GetPublicLotteryConfigHandler(c *gin.Context) {
|
||||||
|
cfg, err := model.GetLotteryConfig()
|
||||||
|
if err != nil {
|
||||||
|
RespJson(c, 500, "获取抽奖配置失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
RespOK(c, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublicLotteryTitleConfigHandler 查询积分抽奖标题(公开)
|
||||||
|
// @Summary 查询积分抽奖标题(公开)
|
||||||
|
// @Tags 积分抽奖
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} model.LotteryTitleConfig
|
||||||
|
// @Router /api/v1/lottery/config/title [post]
|
||||||
|
func GetPublicLotteryTitleConfigHandler(c *gin.Context) {
|
||||||
|
cfg, err := model.GetLotteryTitle()
|
||||||
|
if err != nil {
|
||||||
|
RespJson(c, 500, "获取抽奖配置失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
RespOK(c, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubmitDeliveryInfo 用户填写奖品收货地址
|
||||||
|
// @Summary 用户填写实物奖品收货地址
|
||||||
|
// @Tags 积分抽奖
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param data body model.SubmitDeliveryRequest true "收货信息"
|
||||||
|
// @Success 200 {string} string "提交成功"
|
||||||
|
// @Router /api/v1/lottery/submit_delivery [post]
|
||||||
|
func SubmitDeliveryInfo(c *gin.Context) {
|
||||||
|
uc := auth.GetCurrentUser(c)
|
||||||
|
if uc == nil {
|
||||||
|
RespJson(c, status.Unauthorized, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req model.SubmitDeliveryRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil || req.RecordId == 0 {
|
||||||
|
RespJson(c, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询中奖记录
|
||||||
|
var record model.LotteryRecord
|
||||||
|
if err := model.DB.Where("id = ? AND uid = ?", req.RecordId, uc.Uid).First(&record).Error; err != nil {
|
||||||
|
RespJson(c, 404, "未找到对应的抽奖记录")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if record.PrizeType != model.LotteryPrizeTypePhysical {
|
||||||
|
RespJson(c, 400, "该奖品非实物奖品,无需填写地址")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否已提交过订单
|
||||||
|
var exist model.LotteryPrizeOrder
|
||||||
|
if err := model.DB.Where("record_id = ?", record.ID).First(&exist).Error; err == nil {
|
||||||
|
RespJson(c, 400, "您已提交过收货信息")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询用户手机号
|
||||||
|
userInfo := model.GetUserByUid(uc.Uid)
|
||||||
|
|
||||||
|
// 开启事务
|
||||||
|
tx := model.DB.Begin()
|
||||||
|
if tx.Error != nil {
|
||||||
|
RespJson(c, 500, "事务开启失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建订单
|
||||||
|
order := model.LotteryPrizeOrder{
|
||||||
|
RecordId: uint(record.ID),
|
||||||
|
Uid: record.Uid,
|
||||||
|
Tel: userInfo.Tel,
|
||||||
|
PrizeId: record.PrizeId,
|
||||||
|
PrizeName: record.PrizeName,
|
||||||
|
ReceiverName: req.ReceiverName,
|
||||||
|
ReceiverPhone: req.ReceiverPhone,
|
||||||
|
ReceiverAddr: req.ReceiverAddr,
|
||||||
|
Status: 0, // 待发货
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Create(&order).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
RespJson(c, 500, "保存收货信息失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新抽奖记录状态为处理中(2)
|
||||||
|
if err := tx.Model(&model.LotteryRecord{}).
|
||||||
|
Where("id = ?", record.ID).
|
||||||
|
Update("status", model.LotteryRecordStatusInProcess).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
RespJson(c, 500, "更新抽奖记录状态失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit().Error; err != nil {
|
||||||
|
RespJson(c, 500, "保存收货信息失败:事务提交失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
RespOK(c, "收货信息提交成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfirmLotteryReceipt 用户确认收货
|
||||||
|
// @Summary 用户确认收货
|
||||||
|
// @Tags 积分抽奖
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param data body model.ConfirmReceiptRequest true "收货确认请求"
|
||||||
|
// @Success 200 {string} string "确认成功"
|
||||||
|
// @Router /api/v1/lottery/confirm_receipt [post]
|
||||||
|
func ConfirmLotteryReceipt(c *gin.Context) {
|
||||||
|
uc := auth.GetCurrentUser(c)
|
||||||
|
if uc == nil {
|
||||||
|
RespJson(c, status.Unauthorized, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req model.ConfirmReceiptRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil || req.RecordId == 0 {
|
||||||
|
RespJson(c, 400, "参数错误")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询奖品订单
|
||||||
|
var order model.LotteryPrizeOrder
|
||||||
|
if err := model.DB.Where("record_id = ? AND uid = ?", req.RecordId, uc.Uid).First(&order).Error; err != nil {
|
||||||
|
RespJson(c, 404, "未找到对应的奖品订单")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 状态只能是已发货(1)时才能确认收货
|
||||||
|
if order.Status != 1 {
|
||||||
|
RespJson(c, 400, "当前状态不可确认收货")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
// 开启事务
|
||||||
|
tx := model.DB.Begin()
|
||||||
|
|
||||||
|
// 1. 更新订单状态为 已收货(2)
|
||||||
|
if err := tx.Model(&order).
|
||||||
|
Updates(map[string]interface{}{
|
||||||
|
"status": 2,
|
||||||
|
"received_at": now,
|
||||||
|
}).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
RespJson(c, 500, "确认收货失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 更新抽奖记录状态为 已发放(1)
|
||||||
|
if err := tx.Model(&model.LotteryRecord{}).
|
||||||
|
Where("id = ? AND uid = ?", req.RecordId, uc.Uid).
|
||||||
|
Update("status", model.LotteryRecordStatusDelivered).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
RespJson(c, 500, "同步更新抽奖记录失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit().Error; err != nil {
|
||||||
|
RespJson(c, 500, "确认失败,请稍后重试")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
RespOK(c, "确认收货成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTodayDrawCount 查询用户当天抽奖次数
|
||||||
|
// @Summary 查询用户当天抽奖次数
|
||||||
|
// @Tags 积分抽奖
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} model.TodayDrawCountResponse
|
||||||
|
// @Router /api/v1/lottery/today_draw_count [post]
|
||||||
|
func GetTodayDrawCount(c *gin.Context) {
|
||||||
|
uc := auth.GetCurrentUser(c)
|
||||||
|
if uc == nil {
|
||||||
|
RespJson(c, status.Unauthorized, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当天零点时间
|
||||||
|
startOfDay := time.Now().Truncate(24 * time.Hour)
|
||||||
|
|
||||||
|
var count int64
|
||||||
|
if err := model.DB.Model(&model.LotteryRecord{}).
|
||||||
|
Where("uid = ? AND created_at >= ?", uc.Uid, startOfDay).
|
||||||
|
Count(&count).Error; err != nil {
|
||||||
|
RespJson(c, 500, "查询抽奖次数失败")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := model.TodayDrawCountResponse{
|
||||||
|
DrawCount: int(count),
|
||||||
|
}
|
||||||
|
RespOK(c, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLotteryPrizeOrderDetail 查询抽奖订单详情
|
||||||
|
// @Summary 查询抽奖订单详情
|
||||||
|
// @Tags 积分抽奖
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Param data body model.GetLotteryPrizeOrderDetailRequest true "查询参数"
|
||||||
|
// @Success 200 {object} model.LotteryPrizeOrderDetailResponse
|
||||||
|
// @Router /api/v1/lottery/prize_order/detail [post]
|
||||||
|
func GetLotteryPrizeOrderDetail(c *gin.Context) {
|
||||||
|
uc := auth.GetCurrentUser(c)
|
||||||
|
if uc == nil {
|
||||||
|
RespJson(c, status.Unauthorized, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var req model.GetLotteryPrizeOrderDetailRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil || req.OrderID == 0 {
|
||||||
|
RespJson(c, http.StatusBadRequest, "参数错误:order_id 必传")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var detail model.LotteryPrizeOrderDetailResponse
|
||||||
|
db := model.DB.Table("lottery_prize_order").
|
||||||
|
Select(`lottery_prize_order.*,
|
||||||
|
user.wx_name as nickname,
|
||||||
|
user.tel,
|
||||||
|
user.member_level,
|
||||||
|
store.name as store_name,
|
||||||
|
lottery_prize.prize_type,
|
||||||
|
lottery_prize.level as prize_level,
|
||||||
|
lottery_prize.images,
|
||||||
|
lottery_prize.prize_value`).
|
||||||
|
Joins("LEFT JOIN user ON user.uid = lottery_prize_order.uid").
|
||||||
|
Joins("LEFT JOIN store ON store.id = user.store_id").
|
||||||
|
Joins("LEFT JOIN lottery_prize ON lottery_prize.id = lottery_prize_order.prize_id").
|
||||||
|
Where("lottery_prize_order.record_id = ?", req.OrderID)
|
||||||
|
|
||||||
|
if err := db.First(&detail).Error; err != nil {
|
||||||
|
RespJson(c, http.StatusNotFound, "未找到对应的订单记录")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
RespOK(c, detail)
|
||||||
|
}
|
|
@ -41,6 +41,8 @@ const (
|
||||||
ConfigPaymentGenre = "payment_genre_config" // 支付方式
|
ConfigPaymentGenre = "payment_genre_config" // 支付方式
|
||||||
ConfigNamePrivilegeMember = "privilege_member_config" // 尊享会员配置
|
ConfigNamePrivilegeMember = "privilege_member_config" // 尊享会员配置
|
||||||
ConfigNameOverdueNotice = "overdue_notice" // 超期提示语
|
ConfigNameOverdueNotice = "overdue_notice" // 超期提示语
|
||||||
|
ConfigNameLotteryLimit = "lottery_config" // 积分抽奖配置
|
||||||
|
ConfigNameLotteryTitle = "lottery_title_config" // 积分抽奖标题
|
||||||
)
|
)
|
||||||
|
|
||||||
func PayConfigInfo() (*PayConfig, error) {
|
func PayConfigInfo() (*PayConfig, error) {
|
||||||
|
@ -432,6 +434,52 @@ func GetPaymentGenre() (uint32, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LotteryConfig struct {
|
||||||
|
CostPerDraw uint `json:"cost_per_draw"` // 单次抽奖积分
|
||||||
|
DailyLimit uint `json:"daily_limit"` // 每日抽奖上限
|
||||||
|
LotteryEnabled bool `json:"lottery_enabled"` // 是否开启抽奖功能
|
||||||
|
}
|
||||||
|
|
||||||
|
type LotteryTitleConfig struct {
|
||||||
|
LotteryTitle string `json:"lottery_title"` // 积分抽奖标题
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLotteryConfig() (*LotteryConfig, error) {
|
||||||
|
var config Config
|
||||||
|
err := NewConfigQuerySet(DB).NameEq(ConfigNameLotteryLimit).One(&config)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("读取配置失败:", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var lotteryCfg LotteryConfig
|
||||||
|
err = json.Unmarshal([]byte(config.Value), &lotteryCfg)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("配置解析失败:", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &lotteryCfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLotteryTitle() (*LotteryTitleConfig, error) {
|
||||||
|
var config Config
|
||||||
|
err := NewConfigQuerySet(DB).NameEq(ConfigNameLotteryTitle).One(&config)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("读取配置失败:", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var lotteryCfg LotteryTitleConfig
|
||||||
|
err = json.Unmarshal([]byte(config.Value), &lotteryCfg)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("配置解析失败:", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &lotteryCfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
//type ConfigInterface interface {
|
//type ConfigInterface interface {
|
||||||
// Encode() string
|
// Encode() string
|
||||||
//}
|
//}
|
||||||
|
|
|
@ -630,6 +630,10 @@ func GetGameCardSearch(name string, page, pageSize int, storeId uint32) ([]GameC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cards == nil {
|
||||||
|
cards = make([]GameCard, 0)
|
||||||
|
}
|
||||||
|
|
||||||
return cards, totalPage, nil
|
return cards, totalPage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
237
model/lottery.go
Normal file
237
model/lottery.go
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LotteryPrizeTypePoint = 1 // 积分奖品
|
||||||
|
LotteryPrizeTypeCoupon = 2 // 优惠券奖品
|
||||||
|
LotteryPrizeTypePhysical = 3 // 实物奖品
|
||||||
|
|
||||||
|
LotteryRecordStatusPending = 0 // 待处理
|
||||||
|
LotteryRecordStatusDelivered = 1 // 已发放
|
||||||
|
LotteryRecordStatusInProcess = 2 // 处理中
|
||||||
|
LotteryRecordStatusFailed = 3 // 失败
|
||||||
|
|
||||||
|
LotteryPrizeStatusEnabled = 1 // 启用奖品
|
||||||
|
LotteryPrizeStatusDisabled = 2 // 禁用奖品
|
||||||
|
|
||||||
|
LotteryPrizeTypeNone = 0 // 谢谢参与
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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"` // 奖品图片
|
||||||
|
}
|
||||||
|
|
||||||
|
// LotteryRecord 抽奖记录
|
||||||
|
type LotteryRecord struct {
|
||||||
|
Model
|
||||||
|
|
||||||
|
Uid uint `json:"uid"` // 用户ID
|
||||||
|
PrizeId uint `json:"prize_id"` // 奖品ID
|
||||||
|
PrizeName string `json:"prize_name"` // 奖品名称
|
||||||
|
PrizeType int `json:"prize_type"` // 奖品类型
|
||||||
|
PrizeLevel int `json:"prize_level"` // 奖品等级
|
||||||
|
PrizeValue int `json:"prize_value"` // 奖品值(积分数或券ID等)
|
||||||
|
Status int `json:"status"` // 状态:0-待处理 1-已发放 2-处理中 3-失败
|
||||||
|
IsWin bool `json:"is_win"` // 是否中奖(false 表示“谢谢参与”等无奖项)
|
||||||
|
Images string `json:"images" gorm:"-"` // 奖品图片
|
||||||
|
}
|
||||||
|
|
||||||
|
// LotteryPrizeOrder 抽奖奖品订单(包含用户收件信息、物流信息、发货状态)
|
||||||
|
type LotteryPrizeOrder struct {
|
||||||
|
Model
|
||||||
|
|
||||||
|
RecordId uint `json:"record_id"` // 抽奖记录ID
|
||||||
|
Uid uint `json:"uid"` // 用户ID
|
||||||
|
Tel string `json:"tel"` // 用户手机号
|
||||||
|
PrizeId uint `json:"prize_id"` // 奖品ID
|
||||||
|
PrizeName string `json:"prize_name"` // 奖品名称
|
||||||
|
|
||||||
|
// 用户提交的收货信息
|
||||||
|
ReceiverName string `json:"receiver_name"` // 收件人
|
||||||
|
ReceiverPhone string `json:"receiver_phone"` // 收件人手机号
|
||||||
|
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"` // 收货时间
|
||||||
|
|
||||||
|
Status int `json:"status"` // 发货状态:0-待发货 1-已发货 2-已收货 3-取消
|
||||||
|
}
|
||||||
|
|
||||||
|
// LotteryStats 抽奖统计表
|
||||||
|
type LotteryStats struct {
|
||||||
|
Model
|
||||||
|
|
||||||
|
TotalDraw uint `json:"total_draw"` // 总抽奖数
|
||||||
|
}
|
||||||
|
|
||||||
|
// LotteryWhiteList 抽奖白名单表
|
||||||
|
type LotteryWhiteList struct {
|
||||||
|
Model
|
||||||
|
|
||||||
|
Uid uint `json:"uid"` // 用户ID
|
||||||
|
DrawNumber uint `json:"draw_number"` // 第几次抽奖命中(如第3次)
|
||||||
|
PrizeID uint `json:"prize_id"` // 奖品ID(外键指向 LotteryPrize)
|
||||||
|
}
|
||||||
|
|
||||||
|
type LotteryDrawResponse struct {
|
||||||
|
PrizeID int `json:"prize_id"` // 奖品id
|
||||||
|
PrizeName string `json:"prize_name"` // 奖品名称
|
||||||
|
PrizeLevel int `json:"prize_level"` // 奖品等级
|
||||||
|
Message string `json:"message"` // 中奖提示信息
|
||||||
|
}
|
||||||
|
|
||||||
|
type LotteryPrizeQuery struct {
|
||||||
|
PageNum int `json:"page_num"`
|
||||||
|
PageSize int `json:"page_size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LotteryPrizePageResponse struct {
|
||||||
|
Count uint32 `json:"count"` // 奖品总数
|
||||||
|
PageNum int `json:"page_num"` // 当前页码
|
||||||
|
List []LotteryPrize `json:"list"` // 奖品列表
|
||||||
|
}
|
||||||
|
|
||||||
|
type LotteryRecordQuery struct {
|
||||||
|
PageNum int `json:"page_num"`
|
||||||
|
PageSize int `json:"page_size"`
|
||||||
|
WinStatus int `json:"win_status"` // 新增字段:0=全部,1=已中奖,2=未中奖
|
||||||
|
}
|
||||||
|
|
||||||
|
type LotteryRecordPageResponse struct {
|
||||||
|
Count uint32 `json:"count"` // 总记录数
|
||||||
|
PageNum int `json:"page_num"` // 当前页码
|
||||||
|
List []LotteryRecord `json:"list"` // 抽奖记录列表
|
||||||
|
}
|
||||||
|
|
||||||
|
func DrawFromDatabasePrizes(prizes []LotteryPrize, userDrawCount, globalDrawCount int) (LotteryPrize, error) {
|
||||||
|
var available []LotteryPrize
|
||||||
|
totalWeight := 0
|
||||||
|
|
||||||
|
for _, p := range prizes {
|
||||||
|
// 同时满足两个解锁条件才可参与抽奖
|
||||||
|
if userDrawCount >= p.UnlockUserCount && globalDrawCount >= p.UnlockTotalCount {
|
||||||
|
available = append(available, p)
|
||||||
|
totalWeight += p.Weight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if totalWeight == 0 {
|
||||||
|
return LotteryPrize{}, errors.New("无可抽奖奖品")
|
||||||
|
}
|
||||||
|
|
||||||
|
r := rand.Intn(totalWeight)
|
||||||
|
acc := 0
|
||||||
|
for _, p := range available {
|
||||||
|
acc += p.Weight
|
||||||
|
if r < acc {
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IncrementTotalDraw 更新总抽奖数
|
||||||
|
func IncrementTotalDraw() error {
|
||||||
|
return DB.Exec("UPDATE lottery_stats SET total_draw = total_draw + 1 WHERE id = 1").Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLotteryDrawStats 查询某个用户的抽奖次数和全站总抽奖次数
|
||||||
|
func GetLotteryDrawStats(uid uint32) (userDrawCount int, totalDrawCount int, err error) {
|
||||||
|
// 查询用户抽奖次数
|
||||||
|
err = DB.Model(&LotteryRecord{}).
|
||||||
|
Where("uid = ?", uid).
|
||||||
|
Count(&userDrawCount).Error
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询总抽奖次数(从 lottery_stats 表)
|
||||||
|
var stats LotteryStats
|
||||||
|
err = DB.First(&stats).Error
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
totalDrawCount = int(stats.TotalDraw)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type RecentWinnersQuery struct {
|
||||||
|
Limit int `json:"limit"` // 期望获取的记录数
|
||||||
|
}
|
||||||
|
|
||||||
|
type RecentWinnersResponse struct {
|
||||||
|
Count uint32 `json:"count"` // 实际中奖总数
|
||||||
|
List []LotteryRecord `json:"list"` // 最近中奖记录列表
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubmitDeliveryRequest struct {
|
||||||
|
RecordId uint `json:"record_id" binding:"required"` // 抽奖记录ID,必填
|
||||||
|
ReceiverName string `json:"receiver_name" binding:"required"` // 收件人姓名,必填
|
||||||
|
ReceiverPhone string `json:"receiver_phone" binding:"required"` // 收件人手机号,必填
|
||||||
|
ReceiverAddr string `json:"receiver_addr" binding:"required"` // 收件人地址,必填
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfirmReceiptRequest struct {
|
||||||
|
RecordId uint `json:"record_id" binding:"required"` // 抽奖记录ID,必填
|
||||||
|
}
|
||||||
|
|
||||||
|
type TodayDrawCountResponse struct {
|
||||||
|
DrawCount int `json:"draw_count"` // 今日抽奖次数
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetLotteryPrizeOrderDetailRequest struct {
|
||||||
|
OrderID uint `json:"order_id" binding:"required"` // 奖品订单ID
|
||||||
|
}
|
||||||
|
|
||||||
|
type LotteryPrizeOrderDetailResponse struct {
|
||||||
|
LotteryPrizeOrder
|
||||||
|
|
||||||
|
// 用户信息
|
||||||
|
Nickname string `json:"nickname"` // 用户昵称
|
||||||
|
Tel string `json:"tel"` // 用户手机号
|
||||||
|
MemberLevel uint32 `json:"member_level"` // 当前会员等级
|
||||||
|
StoreName string `json:"store_name"` // 所属门店
|
||||||
|
|
||||||
|
// 奖品信息(扩展字段)
|
||||||
|
PrizeType int `json:"prize_type"` // 奖品类型
|
||||||
|
PrizeLevel int `json:"prize_level"` // 奖品等级
|
||||||
|
PrizeValue int `json:"prize_value"` // 奖品值
|
||||||
|
Images string `json:"images"` // 奖品图片
|
||||||
|
}
|
|
@ -145,6 +145,54 @@ func UserVmUpdate(uid uint32, amount int, event, describe string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func max(a, b int) int {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserVmUpdateTx(tx *gorm.DB, uid uint32, amount int, event, describe string) error {
|
||||||
|
var userVm UserVm
|
||||||
|
err := NewUserVmQuerySet(tx).UidEq(uid).One(&userVm)
|
||||||
|
if err != nil && err != RecordNotFound {
|
||||||
|
logger.Error("积分查询失败:", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err == RecordNotFound {
|
||||||
|
if amount < 0 {
|
||||||
|
return errors.New("积分不足")
|
||||||
|
}
|
||||||
|
userVm = UserVm{Uid: uid, Vm: uint32(amount)}
|
||||||
|
if err := tx.Create(&userVm).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newVm := int(userVm.Vm) + amount
|
||||||
|
if newVm < 0 {
|
||||||
|
newVm = 0
|
||||||
|
}
|
||||||
|
err = tx.Exec("UPDATE user_vm SET vm = ? WHERE uid = ?", newVm, uid).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
record := UserVmRecord{
|
||||||
|
Uid: uid,
|
||||||
|
BeforeVm: userVm.Vm,
|
||||||
|
AfterVm: uint32(max(0, int(userVm.Vm)+amount)),
|
||||||
|
Alter: amount,
|
||||||
|
Event: event,
|
||||||
|
Describe: describe,
|
||||||
|
}
|
||||||
|
if amount > 0 {
|
||||||
|
expireTime := time.Now().AddDate(1, 0, 0)
|
||||||
|
record.ExpiryDate = &expireTime
|
||||||
|
}
|
||||||
|
return tx.Create(&record).Error
|
||||||
|
}
|
||||||
|
|
||||||
// GetUserAvailablePointsRecords 查找用户的所有可用积分记录,按时间排序
|
// GetUserAvailablePointsRecords 查找用户的所有可用积分记录,按时间排序
|
||||||
func GetUserAvailablePointsRecords(uid uint32) []UserVmRecord {
|
func GetUserAvailablePointsRecords(uid uint32) []UserVmRecord {
|
||||||
var userVmRecord []UserVmRecord
|
var userVmRecord []UserVmRecord
|
||||||
|
|
|
@ -323,4 +323,20 @@ func ConfigAppRouter(r gin.IRouter) {
|
||||||
retail.POST("commodity_list", controller.ErpCommodityList)
|
retail.POST("commodity_list", controller.ErpCommodityList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lottery := api.Group("lottery") // 抽奖相关接口
|
||||||
|
{
|
||||||
|
lottery.Use(auth.UserAccessAuth)
|
||||||
|
|
||||||
|
lottery.POST("/draw", controller.LotteryDraw) // 抽奖接口
|
||||||
|
lottery.POST("/records", controller.LotteryRecords) // 抽奖记录接口
|
||||||
|
lottery.POST("/prizes", controller.LotteryPrizes) // 奖品列表接口
|
||||||
|
lottery.POST("/recent_winners", controller.RecentWinners) // 查询最近中奖用户的抽奖记录
|
||||||
|
lottery.POST("/config/public", controller.GetPublicLotteryConfigHandler) // 公开查询抽奖配置
|
||||||
|
lottery.POST("/config/title", controller.GetPublicLotteryTitleConfigHandler) // 公开查询抽奖标题
|
||||||
|
lottery.POST("/submit_delivery", controller.SubmitDeliveryInfo) // 用户填写地址
|
||||||
|
lottery.POST("/confirm_receipt", controller.ConfirmLotteryReceipt) // 用户确认收货
|
||||||
|
lottery.POST("/today_draw_count", controller.GetTodayDrawCount) // 查询用户当天抽奖次数
|
||||||
|
lottery.POST("/prize_order/detail", controller.GetLotteryPrizeOrderDetail) // 查询抽奖订单详情
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user