首页 > 代码库 > 不确定人数的抽奖方法

不确定人数的抽奖方法

很多年前就给前公司的年会做过年会抽奖,基本要求就是年会入场时签到,签到的员工都参与抽奖(也可以设置公司高管过滤,不参与抽奖),奖品是预设好的,到时候就是给所有签到员工编号,然后抽奖过程中不断生成一组随机数,这些随机数对应的编号的员工姓名和照片就显示出来,这是很容易想到的算法。

但是还要一种情况就是互联网模式的抽奖,有点像双十一之前,阿里派发红包一样,大家都可以在开始抽奖的时候去抽,奖品也是预设好的,比如1000W的奖金池,派发完毕就抽奖完毕,每个用户可以抽取多次。这种抽奖方式主要是应对抽奖人数不确定的情况,谁也不需要提前签到报名,到了抽奖时间只要注册用户都可以抽奖。

因为抽奖人数不确定,所以采用一人多次抽奖的方案是很好的,对用户来说也是,如果第一次没有抽中,还可以尝试第二次,第三次。具体算法上,其实更简单,因为用户点击抽奖的顺序是随机的,所以我们连随机数都不用用,直接给用户的一次抽奖请求编个自增的号,如果这个号满足中奖规则,那么就分配礼品,返回该抽奖请求中奖结果,如果不满足中奖规则,那么我们就返回未中奖。

为了避免用户频繁的点击,造成服务器过高的负担,我们可以在客户端设置一个动画过程,比如转盘抽奖,可以转几秒以后才请求服务器,看是否中奖,对用户来说也增加了趣味性。为了避免用户不通过客户端,直接发起频繁的HTTP请求来刷奖,我们甚至可以在服务器设置同一个用户的请求时间间隔。

下面贴出我写的一个示例代码部分,我设置了一个自增的整数Sequence ,每个正常的抽奖请求,则Sequence ++,另外设置默认的抽奖基数baseNumber=100,如果能够Sequence能够被baseNumber整除,那么就中奖,否则不中奖:

[RoutePrefix("api/Lottery")]   public class LotteryController : AbpApiController   {      private static volatile int Sequence = 1;       private static IList<int> winnerList=new List<int>();       /// <summary>       /// 抽奖开始标记,请通过StartNewLotteryRound打开       /// </summary>       private static bool start = false;       /// <summary>       /// 所有产品都被抽完了的标记       /// </summary>       private static bool allPrizeOut = false;       /// <summary>       /// 当前轮次ID       /// </summary>       private static int currentRoundId = 0;       public ILotteryAppService LotteryAppService { get; set; }       /// <summary>       /// 抽奖基数,只要被该数整除就中奖       /// </summary>       private static int baseNumber =100;       private static IDictionary<int,DateTime> userDrawTime=new Dictionary<int, DateTime>();      private bool CheckUserDrawTime(int userId)       {           if (userDrawTime.ContainsKey(userId))           {               return userDrawTime[userId].AddSeconds(8) < DateTime.Now;//8s后可以抽奖           }           else           {               return true;           }       }       /// <summary>       /// 抽奖一次       /// </summary>       /// <param name="userId"></param>       /// <returns></returns>       [HttpGet]       [Route("Draw/{userId}")]       public DrawResult Draw(int userId)       {           if (!start)           {               return new DrawResult(400,0, "抽奖未开始");           }           if (allPrizeOut)           {               return new DrawResult(400, 0, "所有奖品已抽完");           }           if (!CheckUserDrawTime(userId))           {               return new DrawResult(400, 0, "请求过于频繁,请稍后再试");           }          int myNumber = Sequence++;           userDrawTime[userId] = DateTime.Now;//记录用户这次抽奖的时间          if (myNumber%baseNumber == 0) //中奖啦!           {               if (winnerList.Contains(userId))               {                   //用户已经中奖,不用再抽                   return new DrawResult(200, 0, "您已经中过奖了");               }               var result = LotteryAppService.WriteAWinner(userId, currentRoundId);              switch (result.ExceptionType)               {                   case LotteryExceptionType.NoException:                   {                       winnerList.Add(userId);                      return new DrawResult(200, result.PrizeId, "");                   }                   case LotteryExceptionType.AllPrizeOut:                   {                       allPrizeOut = true;                       return new DrawResult(400, 0, "所有奖品已抽完");                  }                   case LotteryExceptionType.InvalidLotteryRound:                   {                       return new DrawResult(400, 0, "抽奖轮次无效");                   }                   default:                   {                       return new DrawResult(400, 0, "当前用户无效");                   }              }          }           return new DrawResult(200, 0, "");      }      /// <summary>       /// 获得我的奖品对象       /// </summary>       /// <param name="userId"></param>       /// <returns></returns>       [HttpGet]       [Route("MyPrize/{userId}")]       public IList<LotteryDto> GetMyPrize(int userId)       {           return LotteryAppService.GetMyPrize(userId);       }      /// <summary>       /// 开始新一轮的抽奖       /// </summary>       /// <param name="roundId"></param>       [HttpPost]       [Route("StartNewLotteryRound")]       [AbpApiAuthorize(PermissionNames.Admin)]       public bool StartNewLotteryRound(int roundId)       {                  start = true;           allPrizeOut = false;           currentRoundId = roundId;           return true;       }       /// <summary>       /// 获得当前轮次的奖品和获奖者       /// </summary>       /// <returns></returns>       [HttpGet]       [Route("")]       public IList<LotteryDto> GetLotteries()       {         return  LotteryAppService.GetLotteries(currentRoundId);       }       /// <summary>       /// 获得所有的奖品和获奖者       /// </summary>       /// <returns></returns>       [HttpGet]       [Route("All")]       public IList<LotteryDto> GetAllLotteries()       {           return LotteryAppService.GetLotteries(0);       }       /// <summary>       /// 清空中奖结果,各种缓存       /// </summary>       /// <returns></returns>       [HttpPost]       [Route("Clean")]       [AbpApiAuthorize(PermissionNames.Admin)]       public bool Clean()       {           Sequence = 1;           start = false;           winnerList.Clear();           LotteryAppService.CleanLotteries();           return true;       }       /// <summary>       /// 获取是否显示抽奖图标       /// </summary>       /// <returns></returns>       [HttpGet]       [Route("ShowLotteryIcon")]       public bool GetShowLotteryIcon()       {           return LotteryAppService.ShowLotteryIcon;       }       /// <summary>       /// 设置是否显示抽奖图标       /// </summary>       /// <param name="show"></param>       /// <returns></returns>       [HttpPut]       [Route("ShowLotteryIcon/{show}")]       public HttpResponseMessage SetShowLotteryIcon(bool show)       {                    try           {               LotteryAppService.ShowLotteryIcon = show;               return Request.CreateResponse(HttpStatusCode.OK, true);           }           catch (Exception ex)           {               var resp = new HttpResponseMessage(HttpStatusCode.BadGateway)               {                   Content = new StringContent("设置ShowLotteryIcon失败:" + ex.Message),                   ReasonPhrase = "Gateway failed"               };               throw new HttpResponseException(resp);           }       }         /// <summary>       /// 设置Base Number       /// </summary>       /// <param name="number"></param>       /// <returns></returns>       [HttpPut]       [AbpApiAuthorize(PermissionNames.Admin)]       [Route("SetBaseNumber/{number}")]       public bool SetBaseNumber(int number)       {           baseNumber = number;           return true;       }   }

 

不确定人数的抽奖方法