package model import "math" const unlimitedRemainingQuotaMB = int64(^uint64(0) >> 1) // PackageTrafficMetrics 套餐流量统一展示值 // 统一封装对外使用的真值、虚值和展示比例,避免各层重复实现公式。 type PackageTrafficMetrics struct { RealTotalMB int64 RealUsedMB int64 VirtualTotalMB int64 VirtualUsedMB float64 ReductionPct float64 } // CalculateDisplayGainRatio 计算展示倍率 // 启用虚流量且虚总量大于 0 时,倍率 = 真总量 / 虚总量;否则退化为 1。 func CalculateDisplayGainRatio(enableVirtualData bool, realTotalMB, virtualTotalMB int64) float64 { if enableVirtualData && virtualTotalMB > 0 { return float64(realTotalMB) / float64(virtualTotalMB) } return 1.0 } // BuildPackageUsageSnapshotValues 计算套餐使用记录应写入的快照值。 func BuildPackageUsageSnapshotValues(pkg *Package) (virtualTotalMB int64, displayGainRatio float64, enableVirtualData bool) { if pkg == nil { return 0, 1.0, false } if pkg.EnableVirtualData && pkg.VirtualDataMB > 0 { return pkg.VirtualDataMB, CalculateDisplayGainRatio(true, pkg.RealDataMB, pkg.VirtualDataMB), true } return pkg.RealDataMB, 1.0, false } // EffectiveThresholdMB 返回套餐业务耗尽阈值。 // 启用虚流量时按虚总量快照判断;否则按真总量判断。 func (u *PackageUsage) EffectiveThresholdMB() int64 { if u == nil { return 0 } if u.EnableVirtualDataSnapshot && u.VirtualTotalMBSnapshot > 0 { return u.VirtualTotalMBSnapshot } return u.DataLimitMB } // RemainingQuotaMB 返回套餐剩余业务额度。 func (u *PackageUsage) RemainingQuotaMB() int64 { if u == nil { return 0 } threshold := u.EffectiveThresholdMB() if threshold <= 0 { return unlimitedRemainingQuotaMB } return threshold - u.DataUsageMB } // IsTrafficDepleted 判断套餐是否达到业务耗尽阈值。 func (u *PackageUsage) IsTrafficDepleted() bool { if u == nil { return false } threshold := u.EffectiveThresholdMB() if threshold <= 0 { return false } return u.DataUsageMB >= threshold } // BuildTrafficMetrics 组装统一五字段和 reduction_pct。 func (u *PackageUsage) BuildTrafficMetrics() PackageTrafficMetrics { metrics := PackageTrafficMetrics{} if u == nil { return metrics } metrics.RealTotalMB = u.DataLimitMB metrics.RealUsedMB = u.DataUsageMB if u.EnableVirtualDataSnapshot && u.VirtualTotalMBSnapshot > 0 { metrics.VirtualTotalMB = u.VirtualTotalMBSnapshot metrics.VirtualUsedMB = math.Min(float64(u.DataUsageMB)*u.DisplayGainRatioSnapshot, float64(u.DataLimitMB)) metrics.ReductionPct = u.DisplayGainRatioSnapshot - 1 return metrics } metrics.VirtualTotalMB = u.DataLimitMB metrics.VirtualUsedMB = float64(u.DataUsageMB) metrics.ReductionPct = 0 return metrics }