zoukankan      html  css  js  c++  java
  • 用.NET Core实现一个类似于饿了吗的简易拆红包功能

     

    需求说明

    以前很讨厌点外卖的我,最近中午经常点外卖,因为确实很方便,提前点好餐,算准时间,就可以在下班的时候吃上饭,然后省下的那些时间就可以在中午的时候多休息一下了。

    点餐结束后,会有一个好友分享红包功能,虽说这个红包不能提现,但却可以抵扣点餐费用,对于经常点餐的人来说,直接用于抵扣现金确实是很大的诱惑,在点餐之后所获得的那个红包,必须要分享出去才能拆。

    那么如果自己也想实现以下抢红包功能,需要说明的是,本文所描述的红包功能更多的关注与随机红包的生成,至于高并发、数据一致性等问题,本文暂未涉及,以下是本文所讨论的两个技术点:

    • 不同的消费金额获取的红包总额不同,消费金额越大,红包总额就越大,红包总数也就越多;
    • 假设有一天,有一种需求是,需要保证参与抢红包的人获得的红包金额在平均数附近波动,也就是尽量的服从正态分布;

    功能实现

    本文描述的场景,所涉及到的金额以分为单位,目的是为了更好的处理随机数。总体的示意图如下:

    totalrobbed

    消费后红包的初始化

    需求重点,用户分享出去的红包总额跟消费总额成正比,可以分拆的子红包个数也与消费总额成正比。

    比如:

    • 10-20元的消费金额,可以分享的单个红包金额为10元,可以供5个人抢
    • 20-40元的消费金额,可以分享的单个红包金额为20元,可以供8个人抢
    • 40-60元的消费金额,可以分享的单个红包金额为30元,可以供10个人抢
    • 60-100元的消费金额,可以分享的单个红包金额为40元,可以供10个人抢
    • 100元以上的消费金额,可以分享的单个红包金额为50元,可以供10个人抢

    那么我们设计出来一个实体,用于表示红包信息,以方便的配置及调整红包规则

       1:  public class RedPacketsInfo
       2:  {
       3:      /// <summary>
       4:      /// 最大消费金额
       5:      /// </summary>
       6:      public int MaxAmount { get; set; }
       7:   
       8:      /// <summary>
       9:      /// 最小消费金额
      10:      /// </summary>
      11:      public int MinAmount { get; set; }
      12:   
      13:      /// <summary>
      14:      /// 红包金额
      15:      /// </summary>
      16:      public int TotalAmount { get; set; }
      17:   
      18:      /// <summary>
      19:      /// 红包可被分割的数量
      20:      /// </summary>
      21:      public int RedPacketQuantity { get; set; }
      22:  }

    红包初始化信息

       1:  private static List<RedPacketsInfo> GetRedPackets()
       2:  {
       3:      return new List<RedPacketsInfo>()
       4:      {
       5:          new RedPacketsInfo
       6:          {
       7:              MinAmount = 1000,
       8:              MaxAmount = 2000,
       9:              RedPacketQuantity = 5,
      10:              TotalAmount=1000
      11:          },
      12:          new RedPacketsInfo
      13:          {
      14:              MinAmount = 2000,
      15:              MaxAmount = 3000,
      16:              RedPacketQuantity = 5,
      17:              TotalAmount=1000
      18:          },
      19:          new RedPacketsInfo
      20:          {
      21:              MinAmount = 4000,
      22:              MaxAmount = 6000,
      23:              RedPacketQuantity = 5,
      24:              TotalAmount=1000
      25:          },
      26:          new RedPacketsInfo
      27:          {
      28:              MinAmount = 6000,
      29:              MaxAmount = 8000,
      30:              RedPacketQuantity = 5,
      31:              TotalAmount=1000
      32:          },
      33:          new RedPacketsInfo
      34:          {
      35:              MinAmount = 10000,
      36:              MaxAmount = int.MaxValue,
      37:              RedPacketQuantity = 5,
      38:              TotalAmount=1000
      39:          }
      40:      };
      41:  }

    接下来我们就可以通过消费金额获取相应的红包信息了。

    随机红包的生成时机及处理

    随机红包的生成可以在抢之前生成也可以在抢的过程中确定,一般而言,很多时候红包会在抢的过程中动态的实际分配,不过在本文中,红包在用户分享成功后会预先生成,主要原因是为了更好地处理处理数据,以使得数据能够服从正态分布。

    以下是其流程图,其中有一段逻辑是回调功能,可能会有圈友会问,如何保证有回调以及回调是成功的,这个地方有很多种处理,比如MQ、任务调度等,此处也不做讨论

    robbed

    那么我们需要设计一个新的实体,以表示分享出去的红包及其生成的随机红包:

       1:  public class SharedRedPacket
       2:  {
       3:      /// <summary>
       4:      /// 分享人UserId
       5:      /// </summary>
       6:      public int SenderUserId { get; set; }
       7:   
       8:      /// <summary>
       9:      /// 分享时间
      10:      /// </summary>
      11:      public DateTime SendTime { get; set; }
      12:   
      13:      public List<RobbedRedPacket> RobbedRedPackets { get; set; }
      14:  }
      15:   
      16:  public class RobbedRedPacket
      17:  {
      18:      /// <summary>
      19:      /// 抢到红包的人的UserId
      20:      /// </summary>
      21:      public int UserId { get; set; }
      22:   
      23:      /// <summary>
      24:      /// 抢到的红包金额
      25:      /// </summary>
      26:      public int Amount { get; set; }
      27:   
      28:      /// <summary>
      29:      /// 抢到时间
      30:      /// </summary>
      31:      public DateTime RobbedTime { get; set; }
      32:  }

    在实现过程中,根据用户消费金额获取相应红包,然后通过随机数,生成n-1个原始的随机数据,最后一个数据用总和减去n-1个数据的和获取到。

       1:  //红包随机拆分
       2:  Random ran = new Random();
       3:  List<double> randoms = new List<double>(redPacketsList.Count);
       4:  for (int i = 0; i < redPacketsInfo.RedPacketQuantity - 1; i++)
       5:  {
       6:      int max = (totalAmount - (redPacketsInfo.RedPacketQuantity - i)) * 1;
       7:      int result = ran.Next(1, max);
       8:      randoms.Add(result);
       9:      totalAmount -= result;
      10:  }
      11:  randoms.Add(totalAmount);

    然后通过设置好系数,以处理数据达到服从正太分布的目的:

       1:  //正太分布处理
       2:  for (int i = 0; i < redPacketsInfo.RedPacketQuantity; i++)
       3:  {
       4:      double a = Math.Sqrt(Math.Abs(2 * Math.Log(randoms[i], Math.E)));
       5:      double b = Math.Cos(2 * Math.PI * randoms[i]);
       6:      randoms[i] = a * b * 0.3 + 1;
       7:  }

    经过第二次处理后,得到的数据与原始数据有偏差,那么我们通过等比例方式再次处理,以确保拆分后的红包总额等于红包原始总额:

       1:  //生成最终的红包数据
       2:  double d = originalTotal / randoms.Sum();
       3:  SharedRedPacket sharedRedPacket = new SharedRedPacket();
       4:  sharedRedPacket.RobbedRedPackets = new List<RobbedRedPacket>(redPacketsList.Count);
       5:  for (int i = 0; i < redPacketsInfo.RedPacketQuantity - 1; i++)
       6:  {
       7:      sharedRedPacket.RobbedRedPackets.Add(new RobbedRedPacket
       8:      {
       9:          Amount = (int)Math.Round(randoms[i] * d, 0)
      10:      });
      11:  }
      12:  sharedRedPacket.RobbedRedPackets.Add(new RobbedRedPacket
      13:  {
      14:      Amount = originalTotal - sharedRedPacket.RobbedRedPackets.Sum(p => p.Amount)
      15:  });

    测试

    测试效果图如下:

    image

    部分代码如下,

       1:  Console.WriteLine("是否分享输入Y分享成功,输入N退出");
       2:  string result = Console.ReadLine();
       3:  if (result == "Y")
       4:  {
       5:      var leftRedPacket = sharedRedPacket.RobbedRedPackets.Where(p => p.UserId <= 0).ToList();
       6:      var robbedRedPacket = leftRedPacket[new Random().Next(1, leftRedPacket.Count + 1)];
       7:      Console.WriteLine("抢到的到红包金额是:" + robbedRedPacket.Amount);
       8:      Console.WriteLine("-------------------------------------------------------");
       9:  }
  • 相关阅读:
    中国的人生路上是紧跟领导就会有回报
    重游三峡广场有感
    假如你没有我
    关于中小型软件企业技术管理的建议(转)
    街客
    游歌乐山有感
    高成就者的反常思维
    漫谈创业和管理-程序员5大思维障碍 (转)
    QQ情缘
    javascript library
  • 原文地址:https://www.cnblogs.com/edison0621/p/11145095.html
Copyright © 2011-2022 走看看