package shanghu import ( "duoduo/apis/shanghu/models" "duoduo/models/shanghu" "duoduo/tools" "duoduo/tools/app" "errors" "fmt" "github.com/gin-gonic/gin" "math/rand" "time" ) // 一个活动对应一个抽奖,一个抽奖对应多个奖品 // 一个活动对应多个拼团,一个拼团对应一个张主图,一个拼团对应多个商品(多个商品是为了核销) func ActiveConfigCreate(c *gin.Context) { var inData models.CreateActiveConfigRequest var sqlData shanghu.MerchantActiveConfig var drawProduct shanghu.MerchantActiveDrawProduct err := c.ShouldBindJSON(&inData) if err != nil { app.Error(c, 400, err, err.Error()) return } if inData.DrawMode != 0 { if len(inData.DrawId) <= 0 { app.Error(c, 500, errors.New("奖品未配置"), "奖品未配置") return } //校验总概率 总概率相加不能大于100 drawList, _, err := drawProduct.GetDrawProductListById(inData.DrawId) if err != nil { app.Error(c, 500, err, err.Error()) return } sumOdds := 0 for _, v := range drawList { sumOdds = sumOdds + v.DrawOdds } if sumOdds != 100 { app.Error(c, 500, errors.New("配置的总中奖率不满100%"), "配置的总中奖率不满100%") return } } if inData.GroupBuyMode == 0 && inData.DrawMode == 0 { app.Error(c, 500, errors.New("请选择抽奖与开团"), "请选择抽奖与开团") return } sqlData.ActivityEndTime, err = tools.TimeToInt64(inData.ActivityEndTime, "2006-01-02") if err != nil { app.Error(c, 400, err, err.Error()) return } sqlData.ActivityStartTime, err = tools.TimeToInt64(inData.ActivityStartTime, "2006-01-02") if err != nil { app.Error(c, 400, err, err.Error()) return } //必中校验 //if inData.DrawOneBiZhong > 0 { // var checkBZ shanghu.MerchantActiveDrawProduct // checkBZ.ID = inData.DrawOneBiZhong // bZInfo, err := checkBZ.GetDrawProductById() // if err != nil { // app.Error(c, 500, err, err.Error()) // return // } // // if !bZInfo.IsPrize { // // app.Error(c, 500, err, err.Error()) // return // } //} sqlData.ActiveName = inData.ActiveName sqlData.MerchantOpenID = inData.MerchantOpenID sqlData.CreatedAt = time.Now() sqlData.UpdatedAt = time.Now() sqlData.ConfigMode = 1 sqlData.DrawOneBiZhong = inData.DrawOneBiZhong sqlData.DrawMode = inData.DrawMode sqlData.GroupBuyMode = inData.GroupBuyMode sqlData.GroupBuyUrl = inData.GroupBuyUrl sqlData.GroupBuy = `{}` sqlData.DrawProduct = `{}` configData, err := sqlData.Create() if err != nil { app.Error(c, 500, err, err.Error()) return } for _, v := range inData.DrawId { var drawData shanghu.MerchantActiveDrawProduct drawData.ActiveConfigID = configData.ID err = drawData.UpdateConfigId(v) if err != nil { app.Error(c, 500, err, err.Error()) return } } for _, v := range inData.GroupBuyId { var groupBuy shanghu.MerchantActiveGroupBuy groupBuy.ActiveConfigID = configData.ID err = groupBuy.UpdateConfigId(v) if err != nil { app.Error(c, 500, err, err.Error()) return } } app.OK(c, nil, app.Success) } // 活动配置信息 func ActiveConfigInfo(c *gin.Context) { var inData models.ActiveConfigRequest var sqlData shanghu.MerchantActiveConfig var outData models.ActiveConfigReply var drawInfoSql shanghu.MerchantActiveDrawProduct var groupBuy shanghu.MerchantActiveGroupBuy err := c.ShouldBindJSON(&inData) if err != nil { app.Error(c, 400, err, err.Error()) return } sqlData.MerchantOpenID = inData.MerchantOpenID sqlData.ID = inData.ActiveConfigId configInfo, err := sqlData.GetConfigInfoById() if err != nil { app.Error(c, 500, err, err.Error()) return } outData.GroupBuyMode = configInfo.GroupBuyMode outData.DrawMode = configInfo.DrawMode outData.GroupBuyUrl = configInfo.GroupBuyUrl outData.DrawOneBiZhong = configInfo.DrawOneBiZhong outData.ActivityEndTime = tools.TimeToStr(configInfo.ActivityEndTime) outData.ActivityStartTime = tools.TimeToStr(configInfo.ActivityStartTime) outData.ActiveName = configInfo.ActiveName //中奖商品 drawInfoSql.ActiveConfigID = configInfo.ID drawInfo, _, err := drawInfoSql.GetDrawProductList() if err != nil { app.Error(c, 500, err, err.Error()) return } for _, v := range drawInfo { var drawProduct models.DrawProduct drawProduct.DrawProductName = v.DrawProductName drawProduct.DrawUrl = v.DrawUrl drawProduct.DrawOdds = v.DrawOdds drawProduct.IsPrize = v.IsPrize drawProduct.TotalStock = v.TotalStock drawProduct.Stock = v.Stock outData.DrawProduct = append(outData.DrawProduct, drawProduct) } // 查询拼团列表 groupBuy.ActiveConfigID = configInfo.ID groupBuyInfoList, _, err := groupBuy.GetGroupBuyList() if err != nil { app.Error(c, 500, err, err.Error()) return } for _, v := range groupBuyInfoList { var groupBuyInfo models.DrawGroupBuy var groupBuyProject shanghu.MerchantActiveGroupByProject groupBuyInfo.GroupBuyName = v.GroupBuyName groupBuyInfo.GroupBuyMode = v.GroupBuyMode groupBuyInfo.GroupBuyUrl = v.GroupBuyUrl groupBuyInfo.GroupBuyThreeNum = v.GroupBuyThreeNum groupBuyInfo.GroupBuyThreePrice = v.GroupBuyThreePrice groupBuyInfo.GroupBuyFourNum = v.GroupBuyFourNum groupBuyInfo.GroupBuyFourPrice = v.GroupBuyFourPrice groupBuyInfo.GroupBuyTwoNum = v.GroupBuyTwoNum groupBuyInfo.GroupBuyTwoPrice = v.GroupBuyTwoPrice groupBuyInfo.GroupBuyOneNum = v.GroupBuyOneNum groupBuyInfo.GroupBuyOnePrice = v.GroupBuyOnePrice groupBuyInfo.MerchantOpenID = v.MerchantOpenID groupBuyInfo.OriginalPrice = v.OriginalPrice groupBuyInfo.RebateRate = v.RebateRate groupBuyProject.GroupBuyID = v.ID projectList, _, err := groupBuyProject.GetGroupBuyProjectList() if err != nil { app.Error(c, 500, err, err.Error()) return } for _, v := range projectList { var groupBuyProjectInfo models.GroupBuyProject groupBuyProjectInfo.ProjectName = v.ProjectName groupBuyProjectInfo.CancelNum = v.CancelNum groupBuyInfo.GroupBuyProject = append(groupBuyInfo.GroupBuyProject, groupBuyProjectInfo) } outData.DrawGroupBuy = append(outData.DrawGroupBuy, groupBuyInfo) } app.OK(c, outData, app.Success) } // 活动列表 func ActiveConfigList(c *gin.Context) { var inData models.ActiveConfigListRequest var sqlData shanghu.MerchantActiveConfig var outData []models.ActiveConfigListReply err := c.ShouldBindJSON(&inData) if err != nil { app.Error(c, 400, err, err.Error()) return } sqlData.MerchantOpenID = inData.OpenId var pageSize = 10 var pageIndex = 1 if inData.PageSize != 0 { pageSize = inData.PageSize } if inData.PageIndex != 0 { pageIndex = inData.PageIndex } activeConfigList, count, err := sqlData.GetActiveConfigList(pageSize, pageIndex) if err != nil { app.Error(c, 500, err, err.Error()) return } for _, v := range activeConfigList { var activeConfig models.ActiveConfigListReply activeConfig.ID = v.ID activeConfig.ActivityStart = tools.TimeToStr(v.ActivityStartTime) activeConfig.ActivityEnd = tools.TimeToStr(v.ActivityEndTime) activeConfig.ActiveName = v.ActiveName activeConfig.CreatedAt = v.CreatedAt.Format(time.DateTime) outData = append(outData, activeConfig) } app.PageOK(c, outData, count, pageIndex, pageSize, app.Success) } // 抽奖 func Draw(c *gin.Context) { var inData models.DrawRequest var sqlData shanghu.MerchantActiveConfig var drawNumSql shanghu.ClientActiveDrawNum var draw shanghu.ClientActiveDrawLog var outData models.DrawReply err := c.ShouldBindJSON(&inData) if err != nil { app.Error(c, 400, err, err.Error()) return } // 校验抽奖次数 drawNumSql.ActiveConfigID = inData.ActiveConfigID drawNumSql.ClientOpenID = inData.ClientOpenID drawNum, err := drawNumSql.GetDrawNum() if err != nil { app.Error(c, 500, err, err.Error()) return } //校验数量 draw.ActiveConfigID = inData.ActiveConfigID draw.ClientOpenID = inData.ClientOpenID clientDrawNum, err := draw.GetClientActiveDrawLogNum() if err != nil { app.Error(c, 500, err, err.Error()) return } if drawNum+1 < clientDrawNum { app.Error(c, 500, errors.New("抽奖次数用完"), "抽奖次数用完") return } // 先校验是否有必中抽奖 sqlData.ID = inData.ActiveConfigID config, err := sqlData.GetConfigInfoById() if err != nil { app.Error(c, 500, err, err.Error()) return } if config.DrawOneBiZhong > 0 { // 必中抽奖需要产看中奖记录是否中过,没有中过直接中,中过了走正常的中奖概率 var drawConfig shanghu.ClientActiveDrawLog drawConfig.ActiveConfigID = inData.ActiveConfigID drawConfig.ClientOpenID = inData.ClientOpenID drawConfig.DrawProductID = config.DrawOneBiZhong count, err := drawConfig.GetClientActiveDrawLogByBiZHong() if err != nil { app.Error(c, 500, err, err.Error()) return } // 为0直接中奖 写中奖记录并且返回 查询中奖 if count == 0 { //var draw shanghu.ClientActiveDrawLog var drawProduct shanghu.MerchantActiveDrawProduct drawProduct.ID = config.DrawOneBiZhong drawProductInfo, err := drawProduct.GetDrawProductById() if err != nil { app.Error(c, 500, err, err.Error()) return } if drawProductInfo.ActiveConfigID != inData.ActiveConfigID { app.Error(c, 500, errors.New("必中配置活动不一致"), "必中配置活动不一致") return } if drawProductInfo.Stock <= 0 { app.Error(c, 500, errors.New("库存不足"), "库存不足") return } fmt.Println(drawProductInfo) //新建log 减库存 err = subStockAddLog(drawProductInfo, inData.ClientOpenID) if err != nil { app.Error(c, 500, err, err.Error()) return } //draw.DrawProductID = config.DrawOneBiZhong //draw.ActiveConfigID = inData.ActiveConfigID //draw.ClientOpenID = inData.ClientOpenID // //if drawProductInfo.IsPrize { // draw.IsPrize = models.NotClaimed //未兑奖 必中一定是要能兑奖的。 //} else { // draw.IsPrize = models.NotWon //未中奖 //} // //draw.CreatedAt = time.Now() //draw.UpdatedAt = time.Now() //_, err = draw.Create() //创建完成 //if err != nil { // app.Error(c, 500, err, err.Error()) // return //} outData.DrawProductName = drawProductInfo.DrawProductName outData.DrawUrl = drawProductInfo.DrawUrl outData.ID = config.DrawOneBiZhong app.OK(c, outData, app.Success) return } } var drawProductList shanghu.MerchantActiveDrawProduct var prizeList []Prize drawProductList.ActiveConfigID = inData.ActiveConfigID drawProductInfo, _, err := drawProductList.GetDrawProductListByActiveId() if err != nil { app.Error(c, 500, err, err.Error()) return } for _, v := range drawProductInfo { var prize Prize if v.Stock <= 0 { continue } prize.Name = v.DrawProductName prize.ID = v.ID prize.Weight = v.DrawOdds prize.InitStock = v.TotalStock prize.Stock = v.Stock prizeList = append(prizeList, prize) } //fmt.Println(prizeList) // 创建抽奖实例 lottery := NewLottery(prizeList) // 执行1次抽奖 results, err := lottery.Draw(1) if err != nil { app.Error(c, 500, err, err.Error()) return } fmt.Println(results) var drawProduct shanghu.MerchantActiveDrawProduct for _, v := range drawProductInfo { if results[0].ID == v.ID { outData.ID = v.ID outData.DrawUrl = v.DrawUrl outData.DrawProductName = v.DrawProductName drawProduct = v break } } //库存减一 并且新增记录 err = subStockAddLog(drawProduct, inData.ClientOpenID) if err != nil { app.Error(c, 500, err, err.Error()) return } app.OK(c, outData, app.Success) } type Prize struct { ID int64 // 奖品ID Name string // 奖品名称 Weight int // 权重 Stock int // 库存数量 InitStock int // 初始库存(用于展示) } // 带权重和库存的抽奖 type Lottery struct { Prizes []Prize TotalWeight int } func NewLottery(prizes []Prize) *Lottery { totalWeight := 0 for _, prize := range prizes { totalWeight += prize.Weight } // 深拷贝奖品列表并初始化初始库存 prizeCopy := make([]Prize, len(prizes)) for i, p := range prizes { prizeCopy[i] = p prizeCopy[i].InitStock = p.Stock } return &Lottery{ Prizes: prizeCopy, TotalWeight: totalWeight, } } func (l *Lottery) Draw(times int) ([]Prize, error) { // 检查总库存是否足够 totalStock := 0 for _, prize := range l.Prizes { totalStock += prize.Stock } if totalStock < times { return nil, fmt.Errorf("剩余总库存(%d)不足以支持%d次抽奖", totalStock, times) } rand.Seed(time.Now().UnixNano()) results := make([]Prize, 0, times) for i := 0; i < times; i++ { // 如果没有可用奖品了就停止 if l.TotalWeight <= 0 { break } randNum := rand.Intn(l.TotalWeight) currentWeight := 0 for j := range l.Prizes { if l.Prizes[j].Stock <= 0 { continue } currentWeight += l.Prizes[j].Weight if randNum < currentWeight { // 中奖 results = append(results, l.Prizes[j]) // 减少库存 l.Prizes[j].Stock-- // 如果库存耗尽,更新总权重 if l.Prizes[j].Stock == 0 { l.TotalWeight -= l.Prizes[j].Weight } break } } } return results, nil } // 新建记录,减库存 func subStockAddLog(drawProductInfo shanghu.MerchantActiveDrawProduct, clientOpenId string) error { var draw shanghu.ClientActiveDrawLog //var drawProduct shanghu.MerchantActiveDrawProduct if drawProductInfo.Stock <= 0 { return errors.New("库存不足") } //减库存 err := drawProductInfo.SubStock() if err != nil { return err } // 新建记录 draw.DrawProductID = drawProductInfo.ID draw.ActiveConfigID = drawProductInfo.ActiveConfigID draw.ClientOpenID = clientOpenId if drawProductInfo.IsPrize { draw.IsPrize = models.NotClaimed //未兑奖 必中一定是要能兑奖的。 } else { draw.IsPrize = models.NotWon //未中奖 } draw.CreatedAt = time.Now() draw.UpdatedAt = time.Now() _, err = draw.Create() //创建完成 if err != nil { return err } return nil }