fix(billing): surface quota saturation events for admin auditing

Thread int32 saturation clamps from tiered settlement and video task
recompute into the consume/task logs under admin_info, so oversized or
malformed billing inputs stay auditable. Clamp negative audio duration
before token conversion and gate the saturation UI markers on admin.
This commit is contained in:
CaIon
2026-07-07 12:20:07 +08:00
parent c9943d37ad
commit bae799ccb1
32 changed files with 710 additions and 104 deletions
+8 -3
View File
@@ -50,6 +50,7 @@ func LogTaskConsumption(c *gin.Context, info *relaycommon.RelayInfo) {
other["is_model_mapped"] = true
other["upstream_model_name"] = info.UpstreamModelName
}
attachQuotaSaturation(c, info, other)
model.RecordConsumeLog(c, info.UserId, model.RecordConsumeLogParams{
ChannelId: info.ChannelId,
ModelName: info.OriginModelName,
@@ -184,7 +185,8 @@ func RefundTaskQuota(ctx context.Context, task *model.Task, reason string) {
// RecalculateTaskQuota 通用的异步差额结算。
// actualQuota 是任务完成后的实际应扣额度,与预扣额度 (task.Quota) 做差额结算。
// reason 用于日志记录(例如 "token重算" 或 "adaptor调整")。
func RecalculateTaskQuota(ctx context.Context, task *model.Task, actualQuota int, reason string) {
// clamps 可选:若计算 actualQuota 时发生额度饱和,将其记入日志 admin_info(仅管理员可见)。
func RecalculateTaskQuota(ctx context.Context, task *model.Task, actualQuota int, reason string, clamps ...*common.QuotaClamp) {
if actualQuota <= 0 {
return
}
@@ -234,6 +236,9 @@ func RecalculateTaskQuota(ctx context.Context, task *model.Task, actualQuota int
other["task_id"] = task.TaskID
other["pre_consumed_quota"] = preConsumedQuota
other["actual_quota"] = actualQuota
for _, clamp := range clamps {
attachQuotaSaturationToOther(other, clamp)
}
model.RecordTaskBillingLog(model.RecordTaskBillingLogParams{
UserId: task.UserId,
LogType: logType,
@@ -298,8 +303,8 @@ func RecalculateTaskQuotaByTokens(ctx context.Context, task *model.Task, totalTo
}
// 计算实际应扣费额度: totalTokens * modelRatio * groupRatio * otherMultiplier(饱和转换,防止溢出成负数)
actualQuota := common.QuotaFromFloat(float64(totalTokens) * modelRatio * finalGroupRatio * otherMultiplier)
actualQuota, clamp := common.QuotaFromFloatChecked(float64(totalTokens) * modelRatio * finalGroupRatio * otherMultiplier)
reason := fmt.Sprintf("token重算:tokens=%d, modelRatio=%.2f, groupRatio=%.2f, otherMultiplier=%.4f", totalTokens, modelRatio, finalGroupRatio, otherMultiplier)
RecalculateTaskQuota(ctx, task, actualQuota, reason)
RecalculateTaskQuota(ctx, task, actualQuota, reason, clamp)
}