fix(billing): validate quantity parameters and harden quota calculations

Bound user-supplied count/duration parameters at request validation,
route ratio multipliers through guarded setters, and use saturating
int conversions in all quota math paths.
This commit is contained in:
CaIon
2026-07-07 00:21:06 +08:00
parent 45f0484dc1
commit d0bd8aac74
17 changed files with 293 additions and 19 deletions
+8 -6
View File
@@ -193,13 +193,15 @@ func RelayTaskSubmit(c *gin.Context, info *relaycommon.RelayInfo) (*TaskSubmitRe
}
}
// 6. 将 OtherRatios 应用到基础额度
// 6. 将 OtherRatios 应用到基础额度(饱和转换,防止溢出成负数)
if !common.StringsContains(constant.TaskPricePatches, modelName) {
quotaWithRatios := float64(info.PriceData.Quota)
for _, ra := range info.PriceData.OtherRatios {
if ra != 1.0 {
info.PriceData.Quota = int(float64(info.PriceData.Quota) * ra)
quotaWithRatios *= ra
}
}
info.PriceData.Quota = common.QuotaFromFloat(quotaWithRatios)
}
// 7. 预扣费(仅首次 — 重试时 info.Billing 已存在,跳过)
@@ -261,21 +263,21 @@ func RelayTaskSubmit(c *gin.Context, info *relaycommon.RelayInfo) (*TaskSubmitRe
// 公式: baseQuota × ∏(ratio) — 其中 baseQuota 是不含 OtherRatios 的基础额度。
func recalcQuotaFromRatios(info *relaycommon.RelayInfo, ratios map[string]float64) int {
// 从 PriceData 获取不含 OtherRatios 的基础价格
baseQuota := info.PriceData.Quota
baseQuota := float64(info.PriceData.Quota)
// 先除掉原有的 OtherRatios 恢复基础额度
for _, ra := range info.PriceData.OtherRatios {
if ra != 1.0 && ra > 0 {
baseQuota = int(float64(baseQuota) / ra)
baseQuota /= ra
}
}
// 应用新的 ratios
result := float64(baseQuota)
result := baseQuota
for _, ra := range ratios {
if ra != 1.0 {
result *= ra
}
}
return int(result)
return common.QuotaFromFloat(result)
}
var fetchRespBuilders = map[int]func(c *gin.Context) (respBody []byte, taskResp *dto.TaskError){