|
@@ -0,0 +1,222 @@ |
|
|
|
|
|
package new_user_red_package |
|
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
|
"errors" |
|
|
|
|
|
"fmt" |
|
|
|
|
|
"math" |
|
|
|
|
|
"math/rand" |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
// RedPacketDistributor 红包分发器,负责管理和分发红包 |
|
|
|
|
|
type RedPacketDistributor struct { |
|
|
|
|
|
TotalAmount float64 // 总金额 |
|
|
|
|
|
TotalDays int // 总天数 |
|
|
|
|
|
RemainAmount float64 // 剩余金额 |
|
|
|
|
|
RemainDays int // 剩余天数 |
|
|
|
|
|
DailyAverage float64 // 每日平均金额 (TotalAmount / TotalDays) |
|
|
|
|
|
DistributionHistory []float64 // 历史分发记录,用于调节分发策略 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// DailyRedPacket 每日红包的详细信息 |
|
|
|
|
|
type DailyRedPacket struct { |
|
|
|
|
|
Day int // 当前天数 |
|
|
|
|
|
BaseAmount float64 // 基础金额(未翻倍前) |
|
|
|
|
|
IsMultiplied bool // 是否选择翻倍 |
|
|
|
|
|
Multiplier float64 // 翻倍倍数 |
|
|
|
|
|
FinalAmount float64 // 最终金额 |
|
|
|
|
|
RemainAmount float64 // 剩余金额 |
|
|
|
|
|
SafetyFactor float64 // 安全系数 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// NewRedPacketDistributor 创建新的红包分发器 |
|
|
|
|
|
func NewRedPacketDistributor(totalAmount, remainAmount float64, totalDays, remainDays int, distributionHistory []float64) *RedPacketDistributor { |
|
|
|
|
|
return &RedPacketDistributor{ |
|
|
|
|
|
TotalAmount: totalAmount, |
|
|
|
|
|
TotalDays: totalDays, |
|
|
|
|
|
RemainAmount: remainAmount, |
|
|
|
|
|
RemainDays: remainDays, |
|
|
|
|
|
DailyAverage: totalAmount / float64(totalDays), |
|
|
|
|
|
DistributionHistory: distributionHistory, |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// NewRedPacketDistributorForTest 创建新的红包分发器(TODO::测试使用) |
|
|
|
|
|
func NewRedPacketDistributorForTest(totalAmount float64, totalDays int) *RedPacketDistributor { |
|
|
|
|
|
return &RedPacketDistributor{ |
|
|
|
|
|
TotalAmount: totalAmount, |
|
|
|
|
|
TotalDays: totalDays, |
|
|
|
|
|
RemainAmount: totalAmount, |
|
|
|
|
|
RemainDays: totalDays, |
|
|
|
|
|
DailyAverage: totalAmount / float64(totalDays), |
|
|
|
|
|
DistributionHistory: make([]float64, 0, totalDays), |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// calculateDynamicSafetyFactor 计算动态安全系数 |
|
|
|
|
|
// 该系数用于控制红包金额的波动范围,确保资金分配的合理性 |
|
|
|
|
|
func (r *RedPacketDistributor) calculateDynamicSafetyFactor(currentDay int) float64 { |
|
|
|
|
|
// 基础安全系数 |
|
|
|
|
|
baseSafety := 0.8 |
|
|
|
|
|
|
|
|
|
|
|
// 根据剩余金额与理想剩余金额的比率调整安全系数 |
|
|
|
|
|
remainingRatio := r.RemainAmount / (r.DailyAverage * float64(r.RemainDays)) |
|
|
|
|
|
if remainingRatio > 1.2 { // 剩余金额偏多,可以适当放宽限制 |
|
|
|
|
|
baseSafety += 0.1 |
|
|
|
|
|
} else if remainingRatio < 0.8 { // 剩余金额偏少,需要更谨慎 |
|
|
|
|
|
baseSafety -= 0.1 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 根据历史分发情况调整安全系数 |
|
|
|
|
|
if len(r.DistributionHistory) > 0 { |
|
|
|
|
|
// 计算历史平均分发金额 |
|
|
|
|
|
avgDistributed := 0.0 |
|
|
|
|
|
for _, amount := range r.DistributionHistory { |
|
|
|
|
|
avgDistributed += amount |
|
|
|
|
|
} |
|
|
|
|
|
avgDistributed /= float64(len(r.DistributionHistory)) |
|
|
|
|
|
|
|
|
|
|
|
// 如果历史平均值明显高于每日平均值,降低安全系数 |
|
|
|
|
|
if avgDistributed > r.DailyAverage*1.2 { |
|
|
|
|
|
baseSafety -= 0.05 |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 在后期(进度>70%)逐步降低安全系数,允许更大的波动 |
|
|
|
|
|
progress := float64(currentDay) / float64(r.TotalDays) |
|
|
|
|
|
if progress > 0.7 { |
|
|
|
|
|
baseSafety -= 0.1 * (progress - 0.7) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 确保安全系数在合理范围内 [0.5, 0.95] |
|
|
|
|
|
return math.Max(0.5, math.Min(0.95, baseSafety)) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// calculateMultiplier 计算翻倍倍率 |
|
|
|
|
|
// 根据基础金额、每日平均值和剩余金额动态调整翻倍倍率 |
|
|
|
|
|
func (r *RedPacketDistributor) calculateMultiplier(baseAmount float64) float64 { |
|
|
|
|
|
// 基础金额与每日平均值的比率 |
|
|
|
|
|
ratio := baseAmount / r.DailyAverage |
|
|
|
|
|
// 基础倍率:金额越大,倍率越小 |
|
|
|
|
|
baseMultiplier := 2.5 - (1.5 * math.Min(ratio, 1.0)) |
|
|
|
|
|
|
|
|
|
|
|
// 添加随机浮动,使金额更自然 [0.95, 1.05] |
|
|
|
|
|
randomFactor := 0.95 + rand.Float64()*0.1 |
|
|
|
|
|
|
|
|
|
|
|
// 根据剩余金额状况调整最大倍率 |
|
|
|
|
|
maxMultiplier := 3.0 |
|
|
|
|
|
if r.RemainAmount < r.DailyAverage*float64(r.RemainDays) { |
|
|
|
|
|
// 剩余金额不足时,降低最大倍率 |
|
|
|
|
|
maxMultiplier = 2.0 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 确保最终倍率在合理范围内 [1.1, maxMultiplier] |
|
|
|
|
|
return math.Max(1.1, math.Min(baseMultiplier*randomFactor, maxMultiplier)) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// calculateSafeAmount 计算安全的基础金额 |
|
|
|
|
|
// 通过多个维度的约束确保金额分配的合理性 |
|
|
|
|
|
func (r *RedPacketDistributor) calculateSafeAmount(currentDay int) float64 { |
|
|
|
|
|
// 最后一天直接返回剩余金额 |
|
|
|
|
|
if r.RemainDays <= 1 { |
|
|
|
|
|
return r.RemainAmount |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 获取当前的安全系数 |
|
|
|
|
|
safetyFactor := r.calculateDynamicSafetyFactor(currentDay) |
|
|
|
|
|
// 计算剩余天数的平均金额 |
|
|
|
|
|
avgRemaining := r.RemainAmount / float64(r.RemainDays) |
|
|
|
|
|
|
|
|
|
|
|
// 根据进度调整金额范围 |
|
|
|
|
|
progress := float64(currentDay) / float64(r.TotalDays) |
|
|
|
|
|
// 最小比率:随进度递减,确保后期有足够金额 |
|
|
|
|
|
minRatio := 0.4 + (0.2 * (1.0 - progress)) |
|
|
|
|
|
// 最大比率:随进度递增,允许更大波动 |
|
|
|
|
|
maxRatio := 1.3 + (0.4 * progress) |
|
|
|
|
|
|
|
|
|
|
|
// 计算金额范围 |
|
|
|
|
|
minAmount := avgRemaining * minRatio |
|
|
|
|
|
maxAmount := avgRemaining * maxRatio |
|
|
|
|
|
|
|
|
|
|
|
// 预留安全金额,确保后续天数有基本保障 |
|
|
|
|
|
safeReserve := r.RemainAmount * 0.3 / float64(r.RemainDays) |
|
|
|
|
|
maxAllowed := r.RemainAmount - (safeReserve * float64(r.RemainDays-1)) |
|
|
|
|
|
|
|
|
|
|
|
// 在允许范围内随机生成金额 |
|
|
|
|
|
randomRatio := rand.Float64() * safetyFactor |
|
|
|
|
|
amount := minAmount + randomRatio*(maxAmount-minAmount) |
|
|
|
|
|
|
|
|
|
|
|
// 确保不超过最大允许金额 |
|
|
|
|
|
return math.Min(amount, maxAllowed) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// DistributeRedPacket 分发红包 |
|
|
|
|
|
// 根据用户选择和各种约束条件计算最终的红包金额 |
|
|
|
|
|
func (r *RedPacketDistributor) DistributeRedPacket(day int, userChooseMultiply bool) (DailyRedPacket, error) { |
|
|
|
|
|
// 检查是否还有红包可分发 |
|
|
|
|
|
if r.RemainDays <= 0 || r.RemainAmount <= 0 { |
|
|
|
|
|
return DailyRedPacket{}, errors.New("当前红包已经发完!") |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 初始化结果 |
|
|
|
|
|
result := DailyRedPacket{ |
|
|
|
|
|
Day: day, |
|
|
|
|
|
IsMultiplied: false, |
|
|
|
|
|
Multiplier: 1.0, |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 计算安全系数 |
|
|
|
|
|
safetyFactor := r.calculateDynamicSafetyFactor(day) |
|
|
|
|
|
result.SafetyFactor = safetyFactor |
|
|
|
|
|
|
|
|
|
|
|
// 计算基础金额 |
|
|
|
|
|
if r.RemainDays == 1 { |
|
|
|
|
|
// 最后一天,分配所有剩余金额 |
|
|
|
|
|
result.BaseAmount = r.RemainAmount |
|
|
|
|
|
} else { |
|
|
|
|
|
result.BaseAmount = r.calculateSafeAmount(day) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 处理翻倍逻辑 |
|
|
|
|
|
if r.RemainDays > 1 && userChooseMultiply { |
|
|
|
|
|
result.IsMultiplied = true |
|
|
|
|
|
result.Multiplier = r.calculateMultiplier(result.BaseAmount) |
|
|
|
|
|
result.FinalAmount = result.BaseAmount * result.Multiplier |
|
|
|
|
|
} else { |
|
|
|
|
|
result.FinalAmount = result.BaseAmount |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 最终安全检查 |
|
|
|
|
|
maxAllowed := r.RemainAmount |
|
|
|
|
|
if r.RemainDays > 1 { |
|
|
|
|
|
// 确保为后续天数预留足够金额 |
|
|
|
|
|
minFutureNeed := (r.RemainAmount * 0.2) / float64(r.RemainDays) |
|
|
|
|
|
maxAllowed = r.RemainAmount - (minFutureNeed * float64(r.RemainDays-1)) |
|
|
|
|
|
} |
|
|
|
|
|
result.FinalAmount = math.Min(result.FinalAmount, maxAllowed) |
|
|
|
|
|
|
|
|
|
|
|
// 更新分发器状态 |
|
|
|
|
|
r.RemainAmount -= result.FinalAmount |
|
|
|
|
|
r.RemainDays-- |
|
|
|
|
|
result.RemainAmount = r.RemainAmount |
|
|
|
|
|
|
|
|
|
|
|
// 记录分发历史 |
|
|
|
|
|
r.DistributionHistory = append(r.DistributionHistory, result.FinalAmount) |
|
|
|
|
|
|
|
|
|
|
|
return result, nil |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// formatRedPacketInfo 格式化红包信息输出 |
|
|
|
|
|
func formatRedPacketInfo(packet DailyRedPacket) string { |
|
|
|
|
|
multiplierInfo := "否" |
|
|
|
|
|
if packet.IsMultiplied { |
|
|
|
|
|
multiplierInfo = fmt.Sprintf("是 (%.2f倍)", packet.Multiplier) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return fmt.Sprintf(`第 %d 天红包详情: |
|
|
|
|
|
基础金额: %.2f 元 |
|
|
|
|
|
是否翻倍: %s |
|
|
|
|
|
安全系数: %.2f |
|
|
|
|
|
最终金额: %.2f 元 |
|
|
|
|
|
剩余金额: %.2f 元 |
|
|
|
|
|
`, packet.Day, packet.BaseAmount, multiplierInfo, packet.SafetyFactor, packet.FinalAmount, packet.RemainAmount) |
|
|
|
|
|
} |