package service import ( "context" "time" "github.com/Wei-Shaw/sub2api/internal/pkg/ctxkey" "github.com/Wei-Shaw/sub2api/internal/pkg/timezone" ) type gatewayTokenRequestPricingAtCtxKey struct{} type gatewayTokenRequestBillingGroupCtxKey struct{} // WithGatewayTokenRequestPricing marks a shared-gateway request as token billed // and freezes the downstream pricing instant for its whole lifetime. Media and // metadata-only handlers deliberately do not call this helper. func WithGatewayTokenRequestPricing(ctx context.Context) (context.Context, time.Time) { if ctx == nil { ctx = context.Background() } pricingAt := timezone.Now() ctx = context.WithValue(ctx, gatewayTokenRequestPricingAtCtxKey{}, pricingAt) // 调度过程中可能因 fallback/composite 路由覆盖 ctxkey.Group;计费 D 仍必须 // 使用认证时刻的父分组,和最终 RecordUsage 的计费归属保持一致。 if group, ok := ctx.Value(ctxkey.Group).(*Group); ok && IsGroupContextValid(group) { ctx = context.WithValue(ctx, gatewayTokenRequestBillingGroupCtxKey{}, group) } return ctx, pricingAt } func gatewayTokenRequestPricingAtFromContext(ctx context.Context) (time.Time, bool) { if ctx == nil { return time.Time{}, false } pricingAt, ok := ctx.Value(gatewayTokenRequestPricingAtCtxKey{}).(time.Time) return pricingAt, ok && !pricingAt.IsZero() } // GatewayTokenRequestPricingAtFromContext exposes the frozen instant to // handlers before they detach asynchronous usage-recording work. func GatewayTokenRequestPricingAtFromContext(ctx context.Context) time.Time { pricingAt, _ := gatewayTokenRequestPricingAtFromContext(ctx) return pricingAt } func gatewayTokenRequestBillingGroupFromContext(ctx context.Context) *Group { if ctx == nil { return nil } group, _ := ctx.Value(gatewayTokenRequestBillingGroupCtxKey{}).(*Group) if IsGroupContextValid(group) { return group } return nil }