package deduction import ( "log" "time" "github.com/perfect-panel/ppanel-server/pkg/tool" ) const ( UnitTimeNoLimit = "NoLimit" UnitTimeYear = "Year" UnitTimeMonth = "Month" UnitTimeDay = "Day" UintTimeHour = "Hour" UintTimeMinute = "Minute" ResetCycleNone = 0 ResetCycle1st = 1 ResetCycleMonthly = 2 ResetCycleYear = 3 ) type Subscribe struct { StartTime time.Time ExpireTime time.Time Traffic int64 Download int64 Upload int64 UnitTime string UnitPrice int64 ResetCycle int64 DeductionRatio int64 } type Order struct { Amount int64 Quantity int64 } func CalculateRemainingAmount(sub Subscribe, order Order) int64 { if sub.UnitTime == UnitTimeNoLimit && sub.ResetCycle != 0 { return 0 } log.Printf("开始计算订单剩余价值") // 实际单价 sub.UnitPrice = order.Amount / order.Quantity log.Printf("订阅实际单价: %d", sub.UnitPrice) now := time.Now() switch sub.UnitTime { case UnitTimeNoLimit: log.Printf("订阅不限时长") usedTraffic := sub.Traffic - sub.Download - sub.Upload unitPrice := float64(order.Amount) / float64(sub.Traffic) return int64(float64(usedTraffic) * unitPrice) case UnitTimeYear: log.Printf("订阅时长为年") remainingYears := tool.YearDiff(now, sub.ExpireTime) remainingUnitTimeAmount := calculateRemainingUnitTimeAmount(sub) return int64(remainingYears)*sub.UnitPrice + remainingUnitTimeAmount case UnitTimeMonth: log.Printf("订阅时长为月") remainingMonths := tool.MonthDiff(now, sub.ExpireTime) remainingUnitTimeAmount := calculateRemainingUnitTimeAmount(sub) return int64(remainingMonths)*sub.UnitPrice + remainingUnitTimeAmount } return 0 } func calculateRemainingUnitTimeAmount(sub Subscribe) int64 { now := time.Now() log.Printf("开始计算订阅剩余时长价值") log.Printf("订阅开始时间: %s, 订阅到期时间: %s,订阅流量: %d", sub.StartTime.Format("2006-01-02 15:04:05"), sub.ExpireTime.Format("2006-01-02 15:04:05"), sub.Traffic) trafficWeight, timeWeight := calculateWeights(sub.DeductionRatio) remainingDays, totalDays := getRemainingAndTotalDays(sub, now) remainingTraffic := sub.Traffic - sub.Download - sub.Upload remainingTimeAmount := calculateProportionalAmount(sub.UnitPrice, remainingDays, totalDays) remainingTrafficAmount := calculateProportionalAmount(sub.UnitPrice, remainingTraffic, sub.Traffic) log.Printf("订阅剩余天数: %d, 总天数: %d, 剩余流量: %d, 剩余时间价值: %d, 剩余流量价值: %d", remainingDays, totalDays, remainingTraffic, remainingTimeAmount, remainingTrafficAmount) if sub.Traffic == 0 { return remainingTimeAmount } if sub.DeductionRatio != 0 { return calculateWeightedAmount(sub.UnitPrice, remainingTraffic, sub.Traffic, remainingDays, totalDays, trafficWeight, timeWeight) } return min(remainingTimeAmount, remainingTrafficAmount) } func calculateWeights(deductionRatio int64) (float64, float64) { if deductionRatio == 0 { return 0, 0 } trafficWeight := float64(deductionRatio) / 100 timeWeight := 1 - trafficWeight return trafficWeight, timeWeight } func getRemainingAndTotalDays(sub Subscribe, now time.Time) (int64, int64) { log.Printf("开始计算订阅剩余天数") log.Printf("重置周期: %d", sub.ResetCycle) switch sub.ResetCycle { case ResetCycleNone: remaining := sub.ExpireTime.Sub(now).Hours() / 24 total := sub.ExpireTime.Sub(sub.StartTime).Hours() / 24 return int64(remaining), int64(total) case ResetCycle1st: return tool.DaysToNextMonth(now), tool.GetLastDayOfMonth(now) case ResetCycleMonthly: // -1 to include the current day return tool.DaysToMonthDay(now, sub.StartTime.Day()) - 1, tool.DaysToMonthDay(now, sub.StartTime.Day()) case ResetCycleYear: return tool.DaysToYearDay(now, int(sub.StartTime.Month()), sub.StartTime.Day()), tool.GetYearDays(now, int(sub.StartTime.Month()), sub.StartTime.Day()) } return 0, 0 } func calculateWeightedAmount(unitPrice, remainingTraffic, totalTraffic, remainingDays, totalDays int64, trafficWeight, timeWeight float64) int64 { remainingTimeRatio := float64(remainingDays) / float64(totalDays) remainingTrafficRatio := float64(remainingTraffic) / float64(totalTraffic) weightedRemainingRatio := (timeWeight * remainingTimeRatio) + (trafficWeight * remainingTrafficRatio) return int64(float64(unitPrice) * weightedRemainingRatio) } func calculateProportionalAmount(unitPrice, remaining, total int64) int64 { return int64(float64(unitPrice) * (float64(remaining) / float64(total))) }