fix: prevent float-to-int truncation turning small billing amounts (e.g. 0.01) into 0 in use-price paths\n\n- service/quota.go: replace IntPart() with Round(0).IntPart() + safety net\n- relay/helper/price.go: replace int(float64) with int(math.Round()) + safety net\n- controller/channel-test.go: same fix for test billing calculation\n- All use-price paths now round instead of truncate, and guarantee minimum 1 quota when effective pricing > 0

This commit is contained in:
xiezhouwei 2026-06-23 11:26:00 +08:00
parent 648c9917ab
commit 789f3b43e4
3 changed files with 27 additions and 5 deletions

View File

@ -645,7 +645,11 @@ func testChannel(channel *model.Channel, testModel string, endpointType string,
quota = 1
}
} else {
quota = int(priceData.ModelPrice * common.QuotaPerUnit)
quota = int(math.Round(priceData.ModelPrice * common.QuotaPerUnit))
// 有有效定价但舍入为 0 时保底为 1
if quota == 0 && priceData.ModelPrice > 0 {
quota = 1
}
}
tok := time.Now()
milliseconds := tok.Sub(tik).Milliseconds()

View File

@ -172,7 +172,11 @@ func ModelPriceHelper(c *gin.Context, info *relaycommon.RelayInfo, promptTokens
effModelPrice = effModelPrice * meta.ImagePriceRatio
modelPrice = modelPrice * meta.ImagePriceRatio // 保持 ModelPrice 字段与 imagePriceRatio 一致(供日志展示)
}
preConsumedQuota = int(effModelPrice * common.QuotaPerUnit * groupRatioInfo.GroupRatio)
preConsumedQuota = int(math.Round(effModelPrice * common.QuotaPerUnit * groupRatioInfo.GroupRatio))
// 有有效定价但舍入为 0 时保底为 1避免计费为 0
if preConsumedQuota == 0 && effModelPrice > 0 && groupRatioInfo.GroupRatio > 0 {
preConsumedQuota = 1
}
}
// check if free model pre-consume is disabled
@ -276,7 +280,11 @@ func ModelPriceHelperPerCall(c *gin.Context, info *relaycommon.RelayInfo) (types
markupDisc := effectiveMarkupDiscountPercent(c, info, channelID, info.OriginModelName)
globalPrice, _ := ratio_setting.GetModelPrice(info.OriginModelName, false)
effModelPrice := model.EffectiveModelPrice(modelPrice, globalPrice, chDisc, markupDisc)
quota := int(effModelPrice * common.QuotaPerUnit * groupRatioInfo.GroupRatio)
quota := int(math.Round(effModelPrice * common.QuotaPerUnit * groupRatioInfo.GroupRatio))
// 有有效定价但舍入为 0 时保底为 1避免计费为 0
if quota == 0 && effModelPrice > 0 && groupRatioInfo.GroupRatio > 0 {
quota = 1
}
// 免费模型检测(与 ModelPriceHelper 对齐)
freeModel := false

View File

@ -66,7 +66,12 @@ func calculateAudioQuota(info QuotaInfo) int {
// 新公式:固定价格 = 渠道固定价 * 成本折扣率% + 全局固定价 * 加价折扣率%
effModelPrice := model.EffectiveModelPrice(info.ModelPrice, info.GlobalModelPrice, costDisc, markupDisc)
quota := decimal.NewFromFloat(effModelPrice).Mul(quotaPerUnit).Mul(groupRatio)
return int(quota.IntPart())
quotaInt := int(quota.Round(0).IntPart())
// 有有效定价但舍入为 0 时保底为 1避免计费为 0
if info.UsePrice && quotaInt == 0 && quota.GreaterThan(decimal.Zero) {
quotaInt = 1
}
return quotaInt
}
completionRatio := decimal.NewFromFloat(ratio_setting.GetCompletionRatio(info.ModelName))
@ -101,7 +106,12 @@ func calculateAudioQuota(info QuotaInfo) int {
quota = decimal.NewFromInt(1)
}
return int(quota.Round(0).IntPart())
quotaInt := int(quota.Round(0).IntPart())
// 有有效输入倍率但舍入为 0 时保底为 1如 0.01 经 Round 后为 0
if quotaInt == 0 && effInputRate > 0 && quota.GreaterThan(decimal.Zero) {
quotaInt = 1
}
return quotaInt
}
func PreWssConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, usage *dto.RealtimeUsage) error {