mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-15 23:22:59 +08:00
87 lines
3.1 KiB
Go
87 lines
3.1 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
|
|
infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
|
|
)
|
|
|
|
const (
|
|
OpenAIQuotaResetWarningCacheRefreshFailed = "reset_credit_cache_refresh_failed"
|
|
OpenAIQuotaResetWarningAccountRecoveryFailed = "account_state_recovery_failed"
|
|
OpenAIQuotaResetWarningAccountRefreshFailed = "account_state_refresh_failed"
|
|
)
|
|
|
|
type openAIQuotaResetWorkflowQuota interface {
|
|
QueryUsage(ctx context.Context, accountID int64) (*OpenAIQuotaUsage, error)
|
|
CachePostResetSnapshot(ctx context.Context, accountID int64, usage *OpenAIQuotaUsage) error
|
|
}
|
|
|
|
type openAIQuotaResetWorkflowRecoverer interface {
|
|
RecoverAccountState(ctx context.Context, accountID int64, options AccountRecoveryOptions) (*SuccessfulTestRecoveryResult, error)
|
|
}
|
|
|
|
// OpenAIQuotaResetPostProcessResult 汇总手动和自动用卡后的共享恢复结果。
|
|
// WarningCode 允许上游消费成功但本地恢复部分失败时仍然准确呈现状态。
|
|
type OpenAIQuotaResetPostProcessResult struct {
|
|
Quota *OpenAIQuotaUsage
|
|
Account *Account
|
|
CacheRefreshed bool
|
|
AccountStateRecovered bool
|
|
WarningCode string
|
|
}
|
|
|
|
// RunOpenAIQuotaResetPostProcess 按“解除限流、刷新额度缓存、刷新账号行”的固定顺序
|
|
// 执行消费后恢复,避免手动和自动入口在失败语义上逐渐分叉。
|
|
func RunOpenAIQuotaResetPostProcess(
|
|
ctx context.Context,
|
|
accountID int64,
|
|
quota openAIQuotaResetWorkflowQuota,
|
|
recoverer openAIQuotaResetWorkflowRecoverer,
|
|
loadAccount func(context.Context, int64) (*Account, error),
|
|
) OpenAIQuotaResetPostProcessResult {
|
|
result := OpenAIQuotaResetPostProcessResult{}
|
|
if recoverer == nil {
|
|
result.WarningCode = OpenAIQuotaResetWarningAccountRecoveryFailed
|
|
return result
|
|
}
|
|
if _, err := recoverer.RecoverAccountState(ctx, accountID, AccountRecoveryOptions{InvalidateToken: true}); err != nil {
|
|
slog.Warn("openai_quota_reset_account_recovery_failed", "account_id", accountID, "error_code", infraerrors.Reason(err))
|
|
result.WarningCode = OpenAIQuotaResetWarningAccountRecoveryFailed
|
|
return result
|
|
}
|
|
result.AccountStateRecovered = true
|
|
|
|
if quota != nil {
|
|
usage, usageErr := quota.QueryUsage(ctx, accountID)
|
|
switch {
|
|
case usageErr != nil || usage == nil:
|
|
slog.Warn("openai_quota_reset_cache_refresh_failed", "account_id", accountID, "error_code", infraerrors.Reason(usageErr))
|
|
result.WarningCode = OpenAIQuotaResetWarningCacheRefreshFailed
|
|
default:
|
|
if err := quota.CachePostResetSnapshot(ctx, accountID, usage); err != nil {
|
|
slog.Warn("openai_quota_reset_cache_refresh_failed", "account_id", accountID, "error_code", infraerrors.Reason(err))
|
|
result.WarningCode = OpenAIQuotaResetWarningCacheRefreshFailed
|
|
} else {
|
|
result.Quota = usage
|
|
result.CacheRefreshed = true
|
|
}
|
|
}
|
|
}
|
|
|
|
if loadAccount == nil {
|
|
return result
|
|
}
|
|
account, err := loadAccount(ctx, accountID)
|
|
if err != nil {
|
|
slog.Warn("openai_quota_reset_account_refresh_failed", "account_id", accountID, "error_code", infraerrors.Reason(err))
|
|
if result.WarningCode == "" {
|
|
result.WarningCode = OpenAIQuotaResetWarningAccountRefreshFailed
|
|
}
|
|
return result
|
|
}
|
|
result.Account = account
|
|
return result
|
|
}
|