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 }