使用.NET Core實(shí)現(xiàn)餓了嗎拆紅包功能
需求說明
以前很討厭點(diǎn)外賣的我,最近中午經(jīng)常點(diǎn)外賣,因?yàn)榇_實(shí)很方便,提前點(diǎn)好餐,算準(zhǔn)時(shí)間,就可以在下班的時(shí)候吃上飯,然后省下的那些時(shí)間就可以在中午的時(shí)候多休息一下了。
點(diǎn)餐結(jié)束后,會(huì)有一個(gè)好友分享紅包功能,雖說這個(gè)紅包不能提現(xiàn),但卻可以抵扣點(diǎn)餐費(fèi)用,對(duì)于經(jīng)常點(diǎn)餐的人來說,直接用于抵扣現(xiàn)金確實(shí)是很大的誘惑,在點(diǎn)餐之后所獲得的那個(gè)紅包,必須要分享出去才能拆。
那么如果自己也想實(shí)現(xiàn)以下?lián)尲t包功能,需要說明的是,本文所描述的紅包功能更多的關(guān)注與隨機(jī)紅包的生成,至于高并發(fā)、數(shù)據(jù)一致性等問題,本文暫未涉及,以下是本文所討論的兩個(gè)技術(shù)點(diǎn):
不同的消費(fèi)金額獲取的紅包總額不同,消費(fèi)金額越大,紅包總額就越大,紅包總數(shù)也就越多;假設(shè)有一天,有一種需求是,需要保證參與搶紅包的人獲得的紅包金額在平均數(shù)附近波動(dòng),也就是盡量的服從正態(tài)分布;
功能實(shí)現(xiàn)
本文描述的場(chǎng)景,所涉及到的金額以分為單位,目的是為了更好的處理隨機(jī)數(shù)??傮w的示意圖如下:
消費(fèi)后紅包的初始化
需求重點(diǎn),用戶分享出去的紅包總額跟消費(fèi)總額成正比,可以分拆的子紅包個(gè)數(shù)也與消費(fèi)總額成正比。
比如:
10-20元的消費(fèi)金額,可以分享的單個(gè)紅包金額為10元,可以供5個(gè)人搶20-40元的消費(fèi)金額,可以分享的單個(gè)紅包金額為20元,可以供8個(gè)人搶40-60元的消費(fèi)金額,可以分享的單個(gè)紅包金額為30元,可以供10個(gè)人搶60-100元的消費(fèi)金額,可以分享的單個(gè)紅包金額為40元,可以供10個(gè)人搶100元以上的消費(fèi)金額,可以分享的單個(gè)紅包金額為50元,可以供10個(gè)人搶
那么我們?cè)O(shè)計(jì)出來一個(gè)實(shí)體,用于表示紅包信息,以方便的配置及調(diào)整紅包規(guī)則
public class RedPacketsInfo { /// <summary> /// 最大消費(fèi)金額 /// </summary> public int MaxAmount { get; set; } /// <summary> /// 最小消費(fèi)金額 /// </summary> public int MinAmount { get; set; } /// <summary> /// 紅包金額 /// </summary> public int TotalAmount { get; set; } /// <summary> /// 紅包可被分割的數(shù)量 /// </summary> public int RedPacketQuantity { get; set; } }
紅包初始化信息
private static List<RedPacketsInfo> GetRedPackets() { return new List<RedPacketsInfo>() { new RedPacketsInfo { MinAmount = 1000, MaxAmount = 2000, RedPacketQuantity = 5, TotalAmount=1000 }, new RedPacketsInfo { MinAmount = 2000, MaxAmount = 3000, RedPacketQuantity = 5, TotalAmount=1000 }, new RedPacketsInfo { MinAmount = 4000, MaxAmount = 6000, RedPacketQuantity = 5, TotalAmount=1000 }, new RedPacketsInfo { MinAmount = 6000, MaxAmount = 8000, RedPacketQuantity = 5, TotalAmount=1000 }, new RedPacketsInfo { MinAmount = 10000, MaxAmount = int.MaxValue, RedPacketQuantity = 5, TotalAmount=1000 } }; }
接下來我們就可以通過消費(fèi)金額獲取相應(yīng)的紅包信息了。
隨機(jī)紅包的生成時(shí)機(jī)及處理
隨機(jī)紅包的生成可以在搶之前生成也可以在搶的過程中確定,一般而言,很多時(shí)候紅包會(huì)在搶的過程中動(dòng)態(tài)的實(shí)際分配,不過在本文中,紅包在用戶分享成功后會(huì)預(yù)先生成,主要原因是為了更好地處理處理數(shù)據(jù),以使得數(shù)據(jù)能夠服從正態(tài)分布。
以下是其流程圖,其中有一段邏輯是回調(diào)功能,可能會(huì)有圈友會(huì)問,如何保證有回調(diào)以及回調(diào)是成功的,這個(gè)地方有很多種處理,比如MQ、任務(wù)調(diào)度等,此處也不做討論
那么我們需要設(shè)計(jì)一個(gè)新的實(shí)體,以表示分享出去的紅包及其生成的隨機(jī)紅包:
public class SharedRedPacket { /// <summary> /// 分享人UserId /// </summary> public int SenderUserId { get; set; } /// <summary> /// 分享時(shí)間 /// </summary> public DateTime SendTime { get; set; } public List<RobbedRedPacket> RobbedRedPackets { get; set; } } public class RobbedRedPacket { /// <summary> /// 搶到紅包的人的UserId /// </summary> public int UserId { get; set; } /// <summary> /// 搶到的紅包金額 /// </summary> public int Amount { get; set; } /// <summary> /// 搶到時(shí)間 /// </summary> public DateTime RobbedTime { get; set; } }
在實(shí)現(xiàn)過程中,根據(jù)用戶消費(fèi)金額獲取相應(yīng)紅包,然后通過隨機(jī)數(shù),生成n-1個(gè)原始的隨機(jī)數(shù)據(jù),最后一個(gè)數(shù)據(jù)用總和減去n-1個(gè)數(shù)據(jù)的和獲取到
//紅包隨機(jī)拆分 Random ran = new Random(); List<double> randoms = new List<double>(redPacketsList.Count); for (int i = 0; i < redPacketsInfo.RedPacketQuantity - 1; i++) { int max = (totalAmount - (redPacketsInfo.RedPacketQuantity - i)) * 1; int result = ran.Next(1, max); randoms.Add(result); totalAmount -= result; } randoms.Add(totalAmount);
然后通過設(shè)置好系數(shù),以處理數(shù)據(jù)達(dá)到服從正太分布的目的:
//正太分布處理 for (int i = 0; i < redPacketsInfo.RedPacketQuantity; i++) { double a = Math.Sqrt(Math.Abs(2 * Math.Log(randoms[i], Math.E))); double b = Math.Cos(2 * Math.PI * randoms[i]); randoms[i] = a * b * 0.3 + 1; }
經(jīng)過第二次處理后,得到的數(shù)據(jù)與原始數(shù)據(jù)有偏差,那么我們通過等比例方式再次處理,以確保拆分后的紅包總額等于紅包原始總額:
//生成最終的紅包數(shù)據(jù) double d = originalTotal / randoms.Sum(); SharedRedPacket sharedRedPacket = new SharedRedPacket(); sharedRedPacket.RobbedRedPackets = new List<RobbedRedPacket>(redPacketsList.Count); for (int i = 0; i < redPacketsInfo.RedPacketQuantity - 1; i++) { sharedRedPacket.RobbedRedPackets.Add(new RobbedRedPacket { Amount = (int)Math.Round(randoms[i] * d, 0) }); } sharedRedPacket.RobbedRedPackets.Add(new RobbedRedPacket { Amount = originalTotal - sharedRedPacket.RobbedRedPackets.Sum(p => p.Amount) });
測(cè)試
測(cè)試效果圖如下:
部分代碼如下,
Console.WriteLine("是否分享輸入Y分享成功,輸入N退出"); string result = Console.ReadLine(); if (result == "Y") { var leftRedPacket = sharedRedPacket.RobbedRedPackets.Where(p => p.UserId <= 0).ToList(); var robbedRedPacket = leftRedPacket[new Random().Next(1, leftRedPacket.Count + 1)]; Console.WriteLine("搶到的到紅包金額是:" + robbedRedPacket.Amount); Console.WriteLine("-------------------------------------------------------"); }
總結(jié)
以上所述是小編給大家介紹的使用.NET Core實(shí)現(xiàn)餓了嗎拆紅包功能,希望對(duì)大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會(huì)及時(shí)回復(fù)大家的!
相關(guān)文章
輕松解決asp.net用戶ASPNET登錄失敗問題的方法分享
這篇文章介紹了asp.net用戶ASPNET登錄失敗問題的方法,有需要的朋友可以參考一下2013-11-11asp.net中利用ajax獲取動(dòng)態(tài)創(chuàng)建表中文本框的值
通常在做主從表的數(shù)據(jù)錄入中,會(huì)碰到在一個(gè)頁面上同時(shí)錄入主表數(shù)據(jù)和從表數(shù)據(jù),主表的數(shù)據(jù)只有一條,從表的數(shù)據(jù)有一條到多條,這樣就要?jiǎng)討B(tài)創(chuàng)建從表數(shù)據(jù)錄入入口。2010-03-03Asp.net Core 初探(發(fā)布和部署Linux)
這篇文章主要介紹了Asp.net Core 初探(發(fā)布和部署Linux),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-12-12深入淺析WinForm 進(jìn)程、線程及區(qū)別介紹
進(jìn)程是一個(gè)具有獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合的一次運(yùn)行活動(dòng)。線程,有時(shí)被稱為輕量級(jí)進(jìn)程(Lightweight Process,LWP),是程序執(zhí)行流的最小單元。這篇文章主要介紹了WinForm 進(jìn)程、線程的相關(guān)資料,需要的朋友可以參考下2016-09-09TrieTree服務(wù)-組件構(gòu)成及其作用介紹
本文將一步步教你配置和使用TrieTree服務(wù),需要的朋友可以參考下2013-01-01關(guān)于.net環(huán)境下跨進(jìn)程、高頻率讀寫數(shù)據(jù)的問題
最近老大教給我一個(gè)項(xiàng)目,項(xiàng)目要求高頻次地讀寫數(shù)據(jù),數(shù)據(jù)量也不是很大,難點(diǎn)在于這個(gè)規(guī)模的熱點(diǎn)數(shù)據(jù),變化非常頻繁,下面把我的處理方法分享到腳本之家平臺(tái),對(duì).net跨進(jìn)程高頻率讀寫數(shù)據(jù)相關(guān)知識(shí)感興趣的朋友跟隨小編一起學(xué)習(xí)下吧2021-05-05Asp.Mvc?2.0用戶客戶端驗(yàn)證實(shí)例講解(3)
這篇文章主要介紹了Asp.Mvc?2.0實(shí)現(xiàn)客戶端驗(yàn)證功能,本文使用jquery.validate插件進(jìn)行驗(yàn)證,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2015-08-08