Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
113 lines
4.6 KiB
Go
113 lines
4.6 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
|
|
"github.com/Wei-Shaw/sub2api/internal/pkg/ctxkey"
|
|
)
|
|
|
|
// withGatewayProfitControlGate installs the gate only for explicitly marked
|
|
// token requests. This keeps media, metadata, and models-list paths outside
|
|
// the profit-control surface by construction.
|
|
func (s *GatewayService) withGatewayProfitControlGate(ctx context.Context, groupID *int64) context.Context {
|
|
if _, ok := gatewayTokenRequestPricingAtFromContext(ctx); !ok || groupID == nil || *groupID <= 0 {
|
|
return ctx
|
|
}
|
|
if existing, ok := ctx.Value(openAIProfitControlGateCtxKey{}).(*openAIProfitControlGate); ok && existing != nil && existing.groupID == *groupID {
|
|
return ctx
|
|
}
|
|
|
|
group, err := s.resolveProfitControlGroup(ctx, *groupID)
|
|
if err != nil {
|
|
slog.Warn("profit_control_group_load_failed", "group_id", *groupID, "error", err)
|
|
return s.clearForeignProfitControlGate(ctx, groupID)
|
|
}
|
|
if group == nil || !group.ProfitControlEnabled || !profitControlPlatformSupported(group.Platform) {
|
|
return s.clearForeignProfitControlGate(ctx, groupID)
|
|
}
|
|
|
|
pricingAt, _ := gatewayTokenRequestPricingAtFromContext(ctx)
|
|
billingGroup := gatewayTokenRequestBillingGroupFromContext(ctx)
|
|
if billingGroup == nil {
|
|
if ctxGroup, ok := ctx.Value(ctxkey.Group).(*Group); ok && IsGroupContextValid(ctxGroup) {
|
|
billingGroup = ctxGroup
|
|
} else {
|
|
billingGroup = group
|
|
}
|
|
}
|
|
|
|
downstream := billingGroup.RateMultiplier
|
|
if userID, _ := ctx.Value(ctxkey.UserID).(int64); userID > 0 {
|
|
downstream = s.ResolveUserGroupRateMultiplier(ctx, userID, billingGroup.ID, billingGroup.RateMultiplier)
|
|
}
|
|
downstream *= billingGroup.PeakMultiplierAt(pricingAt)
|
|
threshold := clampProfitControlThreshold(downstream * (1 - group.ProfitMinMargin - group.ProfitSafetyBuffer))
|
|
|
|
gate := &openAIProfitControlGate{
|
|
groupID: group.ID,
|
|
platform: group.Platform,
|
|
threshold: threshold,
|
|
pricingAt: pricingAt,
|
|
}
|
|
openAIProfitControlObserverInstance.recordInstall(gate.groupID, gate.platform, gate.threshold)
|
|
return context.WithValue(ctx, openAIProfitControlGateCtxKey{}, gate)
|
|
}
|
|
|
|
func (s *GatewayService) clearForeignProfitControlGate(ctx context.Context, groupID *int64) context.Context {
|
|
existing, ok := ctx.Value(openAIProfitControlGateCtxKey{}).(*openAIProfitControlGate)
|
|
if !ok || existing == nil || groupID == nil || existing.groupID == *groupID {
|
|
return ctx
|
|
}
|
|
return context.WithValue(ctx, openAIProfitControlGateCtxKey{}, (*openAIProfitControlGate)(nil))
|
|
}
|
|
|
|
func (s *GatewayService) resolveProfitControlGroup(ctx context.Context, groupID int64) (*Group, error) {
|
|
if group, ok := ctx.Value(ctxkey.Group).(*Group); ok && IsGroupContextValid(group) && group.ID == groupID {
|
|
return group, nil
|
|
}
|
|
if s.schedulerSnapshot != nil {
|
|
// Lite 读取:门只用平台/倍率/利润/高峰字段,不需要账号计数聚合。
|
|
return s.schedulerSnapshot.GetGroupByIDLite(ctx, groupID)
|
|
}
|
|
return s.resolveGroupByID(ctx, groupID)
|
|
}
|
|
|
|
// GatewayProfitControlVetoLatest performs the terminal post-slot check against
|
|
// the latest scheduler snapshot. Snapshot read failures are deliberately
|
|
// fail-open to preserve availability, but are observable.
|
|
func (s *GatewayService) GatewayProfitControlVetoLatest(ctx context.Context, selected *Account) (*Account, bool, string) {
|
|
return profitControlVetoLatest(ctx, selected, s.schedulerSnapshot)
|
|
}
|
|
|
|
func profitControlVetoLatest(ctx context.Context, selected *Account, snapshot *SchedulerSnapshotService) (*Account, bool, string) {
|
|
gate, _ := ctx.Value(openAIProfitControlGateCtxKey{}).(*openAIProfitControlGate)
|
|
if gate == nil || selected == nil {
|
|
return selected, false, ""
|
|
}
|
|
latest := selected
|
|
if snapshot != nil {
|
|
refreshed, err := snapshot.GetAccount(ctx, selected.ID)
|
|
if err != nil || refreshed == nil {
|
|
slog.Warn("profit_control_account_refresh_failed", "group_id", gate.groupID, "platform", gate.platform, "account_id", selected.ID, "error", err)
|
|
openAIProfitControlObserverInstance.recordRefreshFailure(gate.groupID, gate.platform, gate.threshold)
|
|
} else if !refreshed.UpdatedAt.Before(selected.UpdatedAt) {
|
|
// 选号路径可能已做过 DB recheck,selected 比缓存快照更新鲜;只有
|
|
// 快照不落后时才替换,避免终检把新鲜账号换回较旧的缓存对象。
|
|
latest = refreshed
|
|
}
|
|
}
|
|
vetoed, reason := openAIProfitControlVetoReason(ctx, latest)
|
|
return latest, vetoed, reason
|
|
}
|
|
|
|
func (s *GatewayService) isGatewayAccountProfitEligible(ctx context.Context, account *Account) bool {
|
|
vetoed, _ := openAIProfitControlVetoReason(ctx, account)
|
|
return !vetoed
|
|
}
|
|
|
|
func gatewayProfitControlGateActive(ctx context.Context) bool {
|
|
gate, _ := ctx.Value(openAIProfitControlGateCtxKey{}).(*openAIProfitControlGate)
|
|
return gate != nil
|
|
}
|