Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions service/billing.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,25 @@ func PreConsumeBilling(c *gin.Context, preConsumedQuota int, relayInfo *relaycom
// SettleBilling 执行计费结算。如果 RelayInfo 上有 BillingSession 则通过 session 结算,
// 否则回退到旧的 PostConsumeQuota 路径(兼容按次计费等场景)。
func SettleBilling(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, actualQuota int) error {
return settleBilling(ctx, relayInfo, actualQuota, false)
}

// SettleBillingDiscounted settles a quota value that has already had enterprise
// discount applied by the quota calculator.
func SettleBillingDiscounted(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, actualQuota int) error {
return settleBilling(ctx, relayInfo, actualQuota, true)
}

type discountedSettler interface {
SettleDiscounted(actualQuota int) error
}

func settleBilling(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, actualQuota int, alreadyDiscounted bool) error {
if relayInfo.Billing != nil {
normalizedActual := NormalizeRecordedQuota(ctx, relayInfo, actualQuota)
normalizedActual := actualQuota
if !alreadyDiscounted {
normalizedActual = NormalizeRecordedQuota(ctx, relayInfo, actualQuota)
}
preConsumed := relayInfo.Billing.GetPreConsumedQuota()
delta := normalizedActual - preConsumed

Expand All @@ -99,8 +116,18 @@ func SettleBilling(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, actualQuo
))
}

if err := relayInfo.Billing.Settle(actualQuota); err != nil {
return err
if alreadyDiscounted {
settler, ok := relayInfo.Billing.(discountedSettler)
if !ok {
return fmt.Errorf("billing session does not support discounted settlement")
}
if err := settler.SettleDiscounted(actualQuota); err != nil {
return err
}
} else {
if err := relayInfo.Billing.Settle(actualQuota); err != nil {
return err
}
}

// 发送额度通知(订阅计费使用订阅剩余额度)
Expand Down
12 changes: 11 additions & 1 deletion service/billing_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,22 @@ type BillingSession struct {
// 资金来源和令牌额度分两步提交:若资金来源已提交但令牌调整失败,
// 会标记 fundingSettled 防止 Refund 对已提交的资金来源执行退款。
func (s *BillingSession) Settle(actualQuota int) error {
return s.settle(actualQuota, false)
}

// SettleDiscounted settles a quota value that already has the enterprise
// discount applied by the caller.
func (s *BillingSession) SettleDiscounted(actualQuota int) error {
return s.settle(actualQuota, true)
}

func (s *BillingSession) settle(actualQuota int, alreadyDiscounted bool) error {
s.mu.Lock()
defer s.mu.Unlock()
if s.settled {
return nil
}
if s.discountRate < 1.0 {
if !alreadyDiscounted && s.discountRate < 1.0 {
discountedActual := int(float64(actualQuota) * s.discountRate)
if discountedActual < 1 && actualQuota > 0 {
discountedActual = 1
Expand Down
2 changes: 1 addition & 1 deletion service/quota.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ func PostAudioConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, u
model.UpdateChannelUsedQuota(relayInfo.ChannelId, quota)
}

if err := SettleBilling(ctx, relayInfo, quota); err != nil {
if err := SettleBillingDiscounted(ctx, relayInfo, quota); err != nil {
logger.LogError(ctx, "error settling billing: "+err.Error())
}

Expand Down
2 changes: 1 addition & 1 deletion service/text_quota.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ func PostTextConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, us
model.UpdateChannelUsedQuota(relayInfo.ChannelId, summary.Quota)
}

if err := SettleBilling(ctx, relayInfo, summary.Quota); err != nil {
if err := SettleBillingDiscounted(ctx, relayInfo, summary.Quota); err != nil {
logger.LogError(ctx, "error settling billing: "+err.Error())
}

Expand Down