Files
hfb_price_tool/internal/pricing/calc.go
T
2026-10-25 15:02:41 +08:00

141 lines
3.8 KiB
Go

package pricing
import (
"math"
"strings"
)
// Calculate 只计算号主到手价:参考比例、加速比例、纯币价、物品价和合计。
func (config Config) Calculate(input Input) Result {
cfg := Normalize(config)
coinM := math.Max(input.CoinM, 0)
coinWan := coinM * 100
result := Result{
Items: []ItemLine{},
Valid: false,
}
baseRatio, ok := insuranceBaseRatio(cfg.InsuranceBaseRatios, input.Insurance)
if !ok {
result.Message = "未找到该保险格数的基础比例,请检查规则配置"
result.ItemPrice, result.Items = calculateItemLines(cfg, input)
result.TotalPrice = result.ItemPrice
return result
}
result.InsuranceBaseRatio = baseRatio
result.ConfigPenalty = calculateConfigPenalty(cfg.RatioConfigItems, input.States)
result.CoinCorrection = coinCorrection(cfg.CoinCorrections, coinM)
result.DailyLossAdjustment = dailyLossRatioAdjustment(input.DailyLossM)
referenceRatio := RoundRatio(
baseRatio +
result.ConfigPenalty +
result.CoinCorrection +
result.DailyLossAdjustment,
)
result.ReferenceRatio = referenceRatio
result.MaxRatio = RoundRatio(referenceRatio + cfg.AcceleratedMaxRatio)
result.FinalRatio = referenceRatio
if input.SaleRatio > 0 {
result.FinalRatio = RoundRatio(clampSaleRatio(input.SaleRatio, referenceRatio, result.MaxRatio))
}
if coinWan > 0 && result.FinalRatio > 0 {
result.CoinBasePrice = RoundMoney(coinWan / result.FinalRatio)
}
result.ItemPrice, result.Items = calculateItemLines(cfg, input)
result.TotalPrice = RoundMoney(result.CoinBasePrice + result.ItemPrice)
result.Valid = referenceRatio > 0
if coinM <= 0 {
result.Message = "请输入哈弗币数量"
}
return result
}
func insuranceBaseRatio(items []InsuranceBaseRatio, insurance string) (float64, bool) {
insurance = strings.TrimSpace(insurance)
for _, item := range items {
if item.Insurance == insurance {
return item.Ratio, true
}
}
return 0, false
}
func calculateConfigPenalty(items []RatioConfigItem, states map[string]bool) float64 {
penalty := 0.0
for _, item := range items {
if states[item.Key] {
continue
}
penalty += item.MissingPenalty
}
return RoundRatio(penalty)
}
// coinCorrection 命中最高阈值档,且使用严格大于,与主站发布逻辑保持一致。
func coinCorrection(items []CoinCorrection, coinM float64) float64 {
bestThreshold := -1.0
correction := 0.0
for _, item := range items {
if coinM > item.ThresholdM && item.ThresholdM >= bestThreshold {
bestThreshold = item.ThresholdM
correction = item.Correction
}
}
return correction
}
func dailyLossRatioAdjustment(dailyLossM float64) float64 {
if dailyLossM <= 0 {
return 0
}
adjustment := (dailyLossM - 10) / 10
if adjustment < 0 {
return 0
}
floored := math.Floor(adjustment)
if floored > 4 {
return 4
}
return floored
}
func clampSaleRatio(requested, reference, maxRatio float64) float64 {
if requested < reference {
return reference
}
if requested > maxRatio {
return maxRatio
}
return requested
}
func calculateItemLines(config Config, input Input) (float64, []ItemLine) {
lines := make([]ItemLine, 0, len(config.QuantityItems))
rawTotal := 0.0
for _, item := range config.QuantityItems {
quantity := math.Max(input.Quantities[item.Key], 0)
unitPrice := ParseUnitPrice(item.Price)
free := input.FreeItems[item.Key]
disabled := isItemDisabledForInsurance(item, input.Insurance)
amount := 0.0
if !disabled && !free && quantity > 0 && unitPrice > 0 {
rawTotal += quantity * unitPrice
amount = RoundMoney(quantity * unitPrice)
}
lines = append(lines, ItemLine{
Key: item.Key,
Label: item.Label,
Quantity: quantity,
Unit: item.Unit,
UnitPrice: unitPrice,
Amount: amount,
Free: free,
Disabled: disabled,
})
}
return RoundMoney(rawTotal), lines
}