2026-04-24 22:51:34 +08:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
2026-04-25 00:12:55 +08:00
|
|
|
[Serializable]
|
2026-04-24 22:51:34 +08:00
|
|
|
/// <summary>
|
|
|
|
|
/// 纯整数红包记录
|
|
|
|
|
/// 金额单位:分 (int)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class RedPacketRecord
|
|
|
|
|
{
|
|
|
|
|
public int Phase { get; set; } // 阶段:1 或 2
|
|
|
|
|
public int Index { get; set; } // 第几个包
|
2026-08-09 17:57:46 +08:00
|
|
|
public long Amount { get; set; } // 金额(单位:分,纯整数)
|
2026-04-24 22:51:34 +08:00
|
|
|
|
|
|
|
|
// 方便调试看日志的格式化方法
|
|
|
|
|
public string GetMoneyString()
|
|
|
|
|
{
|
|
|
|
|
return (Amount / 100) + "." + (Amount % 100).ToString("D2");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 纯整数版级联红包算法(支持动态裂变个数)
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class CascadeRedPacketInt
|
|
|
|
|
{
|
|
|
|
|
private Random _random;
|
|
|
|
|
private const int MIN_AMOUNT = 1; // 最小 1 分钱
|
|
|
|
|
|
|
|
|
|
// --- 第一阶段配置 ---
|
|
|
|
|
private int _phase1TotalAmount; // 总金额(分)
|
|
|
|
|
private int _phase1Count; // 第一阶段总人数
|
|
|
|
|
private int _phase1Grabbed = 0; // 已抢人数
|
|
|
|
|
|
|
|
|
|
public CascadeRedPacketInt(int totalMoneyCents, int phase1Count, int? seed = null)
|
|
|
|
|
{
|
|
|
|
|
_random = seed.HasValue ? new Random(seed.Value) : new Random();
|
|
|
|
|
_phase1TotalAmount = totalMoneyCents;
|
|
|
|
|
_phase1Count = phase1Count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 核心方法:执行一次完整的“抢大包 -> 拆小包”
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="subCount">【动态参数】第二轮要裂变出的红包个数</param>
|
|
|
|
|
/// <returns>该用户最终获得的一堆小红包列表</returns>
|
|
|
|
|
public List<RedPacketRecord> GrabFullCycle(int subCount)
|
|
|
|
|
{
|
|
|
|
|
// 1. 第一阶段:从大奖池抢一个金额
|
|
|
|
|
int phase1Amount = GrabPhase1();
|
|
|
|
|
|
|
|
|
|
if (phase1Amount <= 0) return null;
|
|
|
|
|
|
|
|
|
|
// 2. 第二阶段:把这个金额拆分成 subCount 个小包
|
|
|
|
|
return SplitPhase2(phase1Amount, subCount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 第一阶段逻辑(不变)
|
|
|
|
|
/// </summary>
|
|
|
|
|
private int GrabPhase1()
|
|
|
|
|
{
|
|
|
|
|
if (_phase1Grabbed >= _phase1Count) return -1;
|
|
|
|
|
|
|
|
|
|
int amount;
|
|
|
|
|
int remainingCount = _phase1Count - _phase1Grabbed;
|
|
|
|
|
|
|
|
|
|
if (_phase1Grabbed == _phase1Count - 1)
|
|
|
|
|
{
|
|
|
|
|
amount = _phase1TotalAmount;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int avg = _phase1TotalAmount / remainingCount;
|
|
|
|
|
int max = avg * 2;
|
|
|
|
|
int safeMax = _phase1TotalAmount - (remainingCount * MIN_AMOUNT);
|
|
|
|
|
|
|
|
|
|
if (max > safeMax) max = safeMax;
|
|
|
|
|
if (max < MIN_AMOUNT) max = MIN_AMOUNT;
|
|
|
|
|
|
|
|
|
|
amount = _random.Next(MIN_AMOUNT, max + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_phase1TotalAmount -= amount;
|
|
|
|
|
_phase1Grabbed++;
|
|
|
|
|
return amount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 第二阶段逻辑(修改点:接收动态个数)
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="seedAmount">种子金额(第一阶段抢到的钱)</param>
|
|
|
|
|
/// <param name="subCount">要拆分的个数</param>
|
|
|
|
|
private List<RedPacketRecord> SplitPhase2(int seedAmount, int subCount)
|
|
|
|
|
{
|
|
|
|
|
List<RedPacketRecord> subPackets = new List<RedPacketRecord>();
|
|
|
|
|
int remainingAmount = seedAmount;
|
|
|
|
|
|
|
|
|
|
// 如果要求的个数小于等于0,直接把钱全给用户(或者你可以选择报错)
|
|
|
|
|
if (subCount <= 0)
|
|
|
|
|
{
|
|
|
|
|
subPackets.Add(new RedPacketRecord { Phase = 2, Index = 1, Amount = seedAmount });
|
|
|
|
|
return subPackets;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i <= subCount; i++)
|
|
|
|
|
{
|
|
|
|
|
int amount;
|
|
|
|
|
int remainingCount = subCount - i;
|
|
|
|
|
|
|
|
|
|
if (remainingCount == 0)
|
|
|
|
|
{
|
|
|
|
|
// 最后一个包,兜底拿走所有剩余
|
|
|
|
|
amount = remainingAmount;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// 二倍均值法
|
|
|
|
|
int avg = remainingAmount / remainingCount;
|
|
|
|
|
int max = avg * 2;
|
|
|
|
|
|
|
|
|
|
// 边界保护
|
|
|
|
|
int safeMax = remainingAmount - (remainingCount * MIN_AMOUNT);
|
|
|
|
|
if (max > safeMax) max = safeMax;
|
|
|
|
|
if (max < MIN_AMOUNT) max = MIN_AMOUNT;
|
|
|
|
|
|
|
|
|
|
amount = _random.Next(MIN_AMOUNT, max + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
remainingAmount -= amount;
|
|
|
|
|
subPackets.Add(new RedPacketRecord { Phase = 2, Index = i, Amount = amount });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return subPackets;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool HasNext()
|
|
|
|
|
{
|
|
|
|
|
return _phase1Grabbed < _phase1Count;
|
|
|
|
|
}
|
|
|
|
|
}
|