141 lines
3.8 KiB
Go
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
|
|
}
|