Problem:
Upstream channels can reject monitor probes based on client fingerprint
(e.g. "only Claude Code clients allowed"). The monitor had no way to
customize the outgoing request to bypass such restrictions.
Solution:
Introduce reusable request templates that carry extra_headers plus an
optional body override; monitors reference a template and receive a
snapshot copy on apply. Template edits do NOT auto-propagate — users
must click "apply to associated monitors" to refresh snapshots, so a
bad template edit cannot instantly break all production monitors.
Data model (migration 112):
- channel_monitor_request_templates: id, name, provider, description,
extra_headers jsonb, body_override_mode ('off'|'merge'|'replace'),
body_override jsonb. Unique (provider, name).
- channel_monitors: +template_id (FK, ON DELETE SET NULL), +extra_headers,
+body_override_mode, +body_override (the three runtime snapshot fields).
Checker (channel_monitor_checker.go):
- callProvider + runCheckForModel accept a CheckOptions carrying the
snapshot fields. mergeHeaders applies user headers on top of adapter
defaults (forbidden list: Host / Content-Length / Transfer-Encoding /
Connection / Content-Encoding).
- buildRequestBody:
off -> adapter default body
merge -> shallow-merge over default; per-provider deny list
(model/messages/contents) protects the challenge contract
replace -> user body verbatim
- Replace mode skips challenge validation; instead HTTP 2xx + non-empty
extracted response text = operational, empty = failed.
- 4 new unit tests cover all three modes + replace/empty-response case.
Admin API:
- /admin/channel-monitor-templates CRUD + /:id/apply (overwrite snapshot
on all template_id=id monitors, returns affected count).
- channel_monitor request/response DTOs gain the 4 new fields.
Frontend:
- channelMonitorTemplate.ts API client.
- MonitorAdvancedRequestConfig.vue shared component for headers textarea
+ body mode radio + body JSON editor; used by both template and monitor
forms.
- MonitorTemplateManagerDialog.vue: provider tabs, list/create/edit/
delete/apply, live "associated monitors" count per row.
- MonitorFiltersBar: new 模板管理 button next to 新增监控.
- MonitorFormDialog: collapsible 高级 section with template dropdown
(filtered by form.provider, clears on provider change) + embedded
AdvancedRequestConfig. Picking a template copies its fields into the
form (snapshot semantics mirrored on the client).
- i18n zh/en entries for all new copy.
chore: bump version to 0.1.114.32
513 lines
18 KiB
Go
513 lines
18 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
"database/sql"
|
||
"time"
|
||
|
||
dbent "github.com/Wei-Shaw/sub2api/ent"
|
||
"github.com/Wei-Shaw/sub2api/internal/config"
|
||
"github.com/Wei-Shaw/sub2api/internal/payment"
|
||
"github.com/Wei-Shaw/sub2api/internal/pkg/logger"
|
||
"github.com/google/wire"
|
||
"github.com/redis/go-redis/v9"
|
||
)
|
||
|
||
// BuildInfo contains build information
|
||
type BuildInfo struct {
|
||
Version string
|
||
BuildType string
|
||
}
|
||
|
||
// ProvidePricingService creates and initializes PricingService
|
||
func ProvidePricingService(cfg *config.Config, remoteClient PricingRemoteClient) (*PricingService, error) {
|
||
svc := NewPricingService(cfg, remoteClient)
|
||
if err := svc.Initialize(); err != nil {
|
||
// Pricing service initialization failure should not block startup, use fallback prices
|
||
println("[Service] Warning: Pricing service initialization failed:", err.Error())
|
||
}
|
||
return svc, nil
|
||
}
|
||
|
||
// ProvideUpdateService creates UpdateService with BuildInfo
|
||
func ProvideUpdateService(cache UpdateCache, githubClient GitHubReleaseClient, buildInfo BuildInfo) *UpdateService {
|
||
return NewUpdateService(cache, githubClient, buildInfo.Version, buildInfo.BuildType)
|
||
}
|
||
|
||
// ProvideEmailQueueService creates EmailQueueService with default worker count
|
||
func ProvideEmailQueueService(emailService *EmailService) *EmailQueueService {
|
||
return NewEmailQueueService(emailService, 3)
|
||
}
|
||
|
||
// ProvideTokenRefreshService creates and starts TokenRefreshService
|
||
func ProvideTokenRefreshService(
|
||
accountRepo AccountRepository,
|
||
oauthService *OAuthService,
|
||
openaiOAuthService *OpenAIOAuthService,
|
||
geminiOAuthService *GeminiOAuthService,
|
||
antigravityOAuthService *AntigravityOAuthService,
|
||
cacheInvalidator TokenCacheInvalidator,
|
||
schedulerCache SchedulerCache,
|
||
cfg *config.Config,
|
||
tempUnschedCache TempUnschedCache,
|
||
privacyClientFactory PrivacyClientFactory,
|
||
proxyRepo ProxyRepository,
|
||
refreshAPI *OAuthRefreshAPI,
|
||
) *TokenRefreshService {
|
||
svc := NewTokenRefreshService(accountRepo, oauthService, openaiOAuthService, geminiOAuthService, antigravityOAuthService, cacheInvalidator, schedulerCache, cfg, tempUnschedCache)
|
||
// 注入 OpenAI privacy opt-out 依赖
|
||
svc.SetPrivacyDeps(privacyClientFactory, proxyRepo)
|
||
// 注入统一 OAuth 刷新 API(消除 TokenRefreshService 与 TokenProvider 之间的竞争条件)
|
||
svc.SetRefreshAPI(refreshAPI)
|
||
// 调用侧显式注入后台刷新策略,避免策略漂移
|
||
svc.SetRefreshPolicy(DefaultBackgroundRefreshPolicy())
|
||
svc.Start()
|
||
return svc
|
||
}
|
||
|
||
// ProvideClaudeTokenProvider creates ClaudeTokenProvider with OAuthRefreshAPI injection
|
||
func ProvideClaudeTokenProvider(
|
||
accountRepo AccountRepository,
|
||
tokenCache GeminiTokenCache,
|
||
oauthService *OAuthService,
|
||
refreshAPI *OAuthRefreshAPI,
|
||
) *ClaudeTokenProvider {
|
||
p := NewClaudeTokenProvider(accountRepo, tokenCache, oauthService)
|
||
executor := NewClaudeTokenRefresher(oauthService)
|
||
p.SetRefreshAPI(refreshAPI, executor)
|
||
p.SetRefreshPolicy(ClaudeProviderRefreshPolicy())
|
||
return p
|
||
}
|
||
|
||
// ProvideOpenAITokenProvider creates OpenAITokenProvider with OAuthRefreshAPI injection
|
||
func ProvideOpenAITokenProvider(
|
||
accountRepo AccountRepository,
|
||
tokenCache GeminiTokenCache,
|
||
openaiOAuthService *OpenAIOAuthService,
|
||
refreshAPI *OAuthRefreshAPI,
|
||
) *OpenAITokenProvider {
|
||
p := NewOpenAITokenProvider(accountRepo, tokenCache, openaiOAuthService)
|
||
executor := NewOpenAITokenRefresher(openaiOAuthService, accountRepo)
|
||
p.SetRefreshAPI(refreshAPI, executor)
|
||
p.SetRefreshPolicy(OpenAIProviderRefreshPolicy())
|
||
return p
|
||
}
|
||
|
||
// ProvideGeminiTokenProvider creates GeminiTokenProvider with OAuthRefreshAPI injection
|
||
func ProvideGeminiTokenProvider(
|
||
accountRepo AccountRepository,
|
||
tokenCache GeminiTokenCache,
|
||
geminiOAuthService *GeminiOAuthService,
|
||
refreshAPI *OAuthRefreshAPI,
|
||
) *GeminiTokenProvider {
|
||
p := NewGeminiTokenProvider(accountRepo, tokenCache, geminiOAuthService)
|
||
executor := NewGeminiTokenRefresher(geminiOAuthService)
|
||
p.SetRefreshAPI(refreshAPI, executor)
|
||
p.SetRefreshPolicy(GeminiProviderRefreshPolicy())
|
||
return p
|
||
}
|
||
|
||
// ProvideAntigravityTokenProvider creates AntigravityTokenProvider with OAuthRefreshAPI injection
|
||
func ProvideAntigravityTokenProvider(
|
||
accountRepo AccountRepository,
|
||
tokenCache GeminiTokenCache,
|
||
antigravityOAuthService *AntigravityOAuthService,
|
||
refreshAPI *OAuthRefreshAPI,
|
||
tempUnschedCache TempUnschedCache,
|
||
) *AntigravityTokenProvider {
|
||
p := NewAntigravityTokenProvider(accountRepo, tokenCache, antigravityOAuthService)
|
||
executor := NewAntigravityTokenRefresher(antigravityOAuthService)
|
||
p.SetRefreshAPI(refreshAPI, executor)
|
||
p.SetRefreshPolicy(AntigravityProviderRefreshPolicy())
|
||
p.SetTempUnschedCache(tempUnschedCache)
|
||
return p
|
||
}
|
||
|
||
// ProvideDashboardAggregationService 创建并启动仪表盘聚合服务
|
||
func ProvideDashboardAggregationService(repo DashboardAggregationRepository, timingWheel *TimingWheelService, cfg *config.Config) *DashboardAggregationService {
|
||
svc := NewDashboardAggregationService(repo, timingWheel, cfg)
|
||
svc.Start()
|
||
return svc
|
||
}
|
||
|
||
// ProvideUsageCleanupService 创建并启动使用记录清理任务服务
|
||
func ProvideUsageCleanupService(repo UsageCleanupRepository, timingWheel *TimingWheelService, dashboardAgg *DashboardAggregationService, cfg *config.Config) *UsageCleanupService {
|
||
svc := NewUsageCleanupService(repo, timingWheel, dashboardAgg, cfg)
|
||
svc.Start()
|
||
return svc
|
||
}
|
||
|
||
// ProvideAccountExpiryService creates and starts AccountExpiryService.
|
||
func ProvideAccountExpiryService(accountRepo AccountRepository) *AccountExpiryService {
|
||
svc := NewAccountExpiryService(accountRepo, time.Minute)
|
||
svc.Start()
|
||
return svc
|
||
}
|
||
|
||
// ProvideSubscriptionExpiryService creates and starts SubscriptionExpiryService.
|
||
func ProvideSubscriptionExpiryService(userSubRepo UserSubscriptionRepository) *SubscriptionExpiryService {
|
||
svc := NewSubscriptionExpiryService(userSubRepo, time.Minute)
|
||
svc.Start()
|
||
return svc
|
||
}
|
||
|
||
// ProvideTimingWheelService creates and starts TimingWheelService
|
||
func ProvideTimingWheelService() (*TimingWheelService, error) {
|
||
svc, err := NewTimingWheelService()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
svc.Start()
|
||
return svc, nil
|
||
}
|
||
|
||
// ProvideDeferredService creates and starts DeferredService
|
||
func ProvideDeferredService(accountRepo AccountRepository, timingWheel *TimingWheelService) *DeferredService {
|
||
svc := NewDeferredService(accountRepo, timingWheel, 10*time.Second)
|
||
svc.Start()
|
||
return svc
|
||
}
|
||
|
||
// ProvideConcurrencyService creates ConcurrencyService and starts slot cleanup worker.
|
||
func ProvideConcurrencyService(cache ConcurrencyCache, accountRepo AccountRepository, cfg *config.Config) *ConcurrencyService {
|
||
svc := NewConcurrencyService(cache)
|
||
if err := svc.CleanupStaleProcessSlots(context.Background()); err != nil {
|
||
logger.LegacyPrintf("service.concurrency", "Warning: startup cleanup stale process slots failed: %v", err)
|
||
}
|
||
if cfg != nil {
|
||
svc.StartSlotCleanupWorker(accountRepo, cfg.Gateway.Scheduling.SlotCleanupInterval)
|
||
}
|
||
return svc
|
||
}
|
||
|
||
// ProvideUserMessageQueueService 创建用户消息串行队列服务并启动清理 worker
|
||
func ProvideUserMessageQueueService(cache UserMsgQueueCache, rpmCache RPMCache, cfg *config.Config) *UserMessageQueueService {
|
||
svc := NewUserMessageQueueService(cache, rpmCache, &cfg.Gateway.UserMessageQueue)
|
||
if cfg.Gateway.UserMessageQueue.CleanupIntervalSeconds > 0 {
|
||
svc.StartCleanupWorker(time.Duration(cfg.Gateway.UserMessageQueue.CleanupIntervalSeconds) * time.Second)
|
||
}
|
||
return svc
|
||
}
|
||
|
||
// ProvideSchedulerSnapshotService creates and starts SchedulerSnapshotService.
|
||
func ProvideSchedulerSnapshotService(
|
||
cache SchedulerCache,
|
||
outboxRepo SchedulerOutboxRepository,
|
||
accountRepo AccountRepository,
|
||
groupRepo GroupRepository,
|
||
cfg *config.Config,
|
||
) *SchedulerSnapshotService {
|
||
svc := NewSchedulerSnapshotService(cache, outboxRepo, accountRepo, groupRepo, cfg)
|
||
svc.Start()
|
||
return svc
|
||
}
|
||
|
||
// ProvideRateLimitService creates RateLimitService with optional dependencies.
|
||
func ProvideRateLimitService(
|
||
accountRepo AccountRepository,
|
||
usageRepo UsageLogRepository,
|
||
cfg *config.Config,
|
||
geminiQuotaService *GeminiQuotaService,
|
||
tempUnschedCache TempUnschedCache,
|
||
timeoutCounterCache TimeoutCounterCache,
|
||
settingService *SettingService,
|
||
tokenCacheInvalidator TokenCacheInvalidator,
|
||
) *RateLimitService {
|
||
svc := NewRateLimitService(accountRepo, usageRepo, cfg, geminiQuotaService, tempUnschedCache)
|
||
svc.SetTimeoutCounterCache(timeoutCounterCache)
|
||
svc.SetSettingService(settingService)
|
||
svc.SetTokenCacheInvalidator(tokenCacheInvalidator)
|
||
return svc
|
||
}
|
||
|
||
// ProvideOpsMetricsCollector creates and starts OpsMetricsCollector.
|
||
func ProvideOpsMetricsCollector(
|
||
opsRepo OpsRepository,
|
||
settingRepo SettingRepository,
|
||
accountRepo AccountRepository,
|
||
concurrencyService *ConcurrencyService,
|
||
db *sql.DB,
|
||
redisClient *redis.Client,
|
||
cfg *config.Config,
|
||
) *OpsMetricsCollector {
|
||
collector := NewOpsMetricsCollector(opsRepo, settingRepo, accountRepo, concurrencyService, db, redisClient, cfg)
|
||
collector.Start()
|
||
return collector
|
||
}
|
||
|
||
// ProvideOpsAggregationService creates and starts OpsAggregationService (hourly/daily pre-aggregation).
|
||
func ProvideOpsAggregationService(
|
||
opsRepo OpsRepository,
|
||
settingRepo SettingRepository,
|
||
db *sql.DB,
|
||
redisClient *redis.Client,
|
||
cfg *config.Config,
|
||
) *OpsAggregationService {
|
||
svc := NewOpsAggregationService(opsRepo, settingRepo, db, redisClient, cfg)
|
||
svc.Start()
|
||
return svc
|
||
}
|
||
|
||
// ProvideOpsAlertEvaluatorService creates and starts OpsAlertEvaluatorService.
|
||
func ProvideOpsAlertEvaluatorService(
|
||
opsService *OpsService,
|
||
opsRepo OpsRepository,
|
||
emailService *EmailService,
|
||
redisClient *redis.Client,
|
||
cfg *config.Config,
|
||
) *OpsAlertEvaluatorService {
|
||
svc := NewOpsAlertEvaluatorService(opsService, opsRepo, emailService, redisClient, cfg)
|
||
svc.Start()
|
||
return svc
|
||
}
|
||
|
||
// ProvideOpsCleanupService creates and starts OpsCleanupService (cron scheduled).
|
||
// channelMonitorSvc 让维护任务(聚合 + 历史/聚合软删)跟随 ops 清理 cron 一起跑,
|
||
// 共享 leader lock + heartbeat。
|
||
func ProvideOpsCleanupService(
|
||
opsRepo OpsRepository,
|
||
db *sql.DB,
|
||
redisClient *redis.Client,
|
||
cfg *config.Config,
|
||
channelMonitorSvc *ChannelMonitorService,
|
||
) *OpsCleanupService {
|
||
svc := NewOpsCleanupService(opsRepo, db, redisClient, cfg, channelMonitorSvc)
|
||
svc.Start()
|
||
return svc
|
||
}
|
||
|
||
func ProvideOpsSystemLogSink(opsRepo OpsRepository) *OpsSystemLogSink {
|
||
sink := NewOpsSystemLogSink(opsRepo)
|
||
sink.Start()
|
||
logger.SetSink(sink)
|
||
return sink
|
||
}
|
||
|
||
func buildIdempotencyConfig(cfg *config.Config) IdempotencyConfig {
|
||
idempotencyCfg := DefaultIdempotencyConfig()
|
||
if cfg != nil {
|
||
if cfg.Idempotency.DefaultTTLSeconds > 0 {
|
||
idempotencyCfg.DefaultTTL = time.Duration(cfg.Idempotency.DefaultTTLSeconds) * time.Second
|
||
}
|
||
if cfg.Idempotency.SystemOperationTTLSeconds > 0 {
|
||
idempotencyCfg.SystemOperationTTL = time.Duration(cfg.Idempotency.SystemOperationTTLSeconds) * time.Second
|
||
}
|
||
if cfg.Idempotency.ProcessingTimeoutSeconds > 0 {
|
||
idempotencyCfg.ProcessingTimeout = time.Duration(cfg.Idempotency.ProcessingTimeoutSeconds) * time.Second
|
||
}
|
||
if cfg.Idempotency.FailedRetryBackoffSeconds > 0 {
|
||
idempotencyCfg.FailedRetryBackoff = time.Duration(cfg.Idempotency.FailedRetryBackoffSeconds) * time.Second
|
||
}
|
||
if cfg.Idempotency.MaxStoredResponseLen > 0 {
|
||
idempotencyCfg.MaxStoredResponseLen = cfg.Idempotency.MaxStoredResponseLen
|
||
}
|
||
idempotencyCfg.ObserveOnly = cfg.Idempotency.ObserveOnly
|
||
}
|
||
return idempotencyCfg
|
||
}
|
||
|
||
func ProvideIdempotencyCoordinator(repo IdempotencyRepository, cfg *config.Config) *IdempotencyCoordinator {
|
||
coordinator := NewIdempotencyCoordinator(repo, buildIdempotencyConfig(cfg))
|
||
SetDefaultIdempotencyCoordinator(coordinator)
|
||
return coordinator
|
||
}
|
||
|
||
func ProvideSystemOperationLockService(repo IdempotencyRepository, cfg *config.Config) *SystemOperationLockService {
|
||
return NewSystemOperationLockService(repo, buildIdempotencyConfig(cfg))
|
||
}
|
||
|
||
func ProvideIdempotencyCleanupService(repo IdempotencyRepository, cfg *config.Config) *IdempotencyCleanupService {
|
||
svc := NewIdempotencyCleanupService(repo, cfg)
|
||
svc.Start()
|
||
return svc
|
||
}
|
||
|
||
// ProvideScheduledTestService creates ScheduledTestService.
|
||
func ProvideScheduledTestService(
|
||
planRepo ScheduledTestPlanRepository,
|
||
resultRepo ScheduledTestResultRepository,
|
||
) *ScheduledTestService {
|
||
return NewScheduledTestService(planRepo, resultRepo)
|
||
}
|
||
|
||
// ProvideScheduledTestRunnerService creates and starts ScheduledTestRunnerService.
|
||
func ProvideScheduledTestRunnerService(
|
||
planRepo ScheduledTestPlanRepository,
|
||
scheduledSvc *ScheduledTestService,
|
||
accountTestSvc *AccountTestService,
|
||
rateLimitSvc *RateLimitService,
|
||
cfg *config.Config,
|
||
) *ScheduledTestRunnerService {
|
||
svc := NewScheduledTestRunnerService(planRepo, scheduledSvc, accountTestSvc, rateLimitSvc, cfg)
|
||
svc.Start()
|
||
return svc
|
||
}
|
||
|
||
// ProvideOpsScheduledReportService creates and starts OpsScheduledReportService.
|
||
func ProvideOpsScheduledReportService(
|
||
opsService *OpsService,
|
||
userService *UserService,
|
||
emailService *EmailService,
|
||
redisClient *redis.Client,
|
||
cfg *config.Config,
|
||
) *OpsScheduledReportService {
|
||
svc := NewOpsScheduledReportService(opsService, userService, emailService, redisClient, cfg)
|
||
svc.Start()
|
||
return svc
|
||
}
|
||
|
||
// ProvideAPIKeyAuthCacheInvalidator 提供 API Key 认证缓存失效能力
|
||
func ProvideAPIKeyAuthCacheInvalidator(apiKeyService *APIKeyService) APIKeyAuthCacheInvalidator {
|
||
// Start Pub/Sub subscriber for L1 cache invalidation across instances
|
||
apiKeyService.StartAuthCacheInvalidationSubscriber(context.Background())
|
||
return apiKeyService
|
||
}
|
||
|
||
// ProvideBackupService creates and starts BackupService
|
||
func ProvideBackupService(
|
||
settingRepo SettingRepository,
|
||
cfg *config.Config,
|
||
encryptor SecretEncryptor,
|
||
storeFactory BackupObjectStoreFactory,
|
||
dumper DBDumper,
|
||
) *BackupService {
|
||
svc := NewBackupService(settingRepo, cfg, encryptor, storeFactory, dumper)
|
||
svc.Start()
|
||
return svc
|
||
}
|
||
|
||
// ProvideSettingService wires SettingService with group reader and proxy repo.
|
||
func ProvideSettingService(settingRepo SettingRepository, groupRepo GroupRepository, proxyRepo ProxyRepository, cfg *config.Config) *SettingService {
|
||
svc := NewSettingService(settingRepo, cfg)
|
||
svc.SetDefaultSubscriptionGroupReader(groupRepo)
|
||
svc.SetProxyRepository(proxyRepo)
|
||
return svc
|
||
}
|
||
|
||
// ProviderSet is the Wire provider set for all services
|
||
var ProviderSet = wire.NewSet(
|
||
// Core services
|
||
NewAuthService,
|
||
NewUserService,
|
||
NewAPIKeyService,
|
||
ProvideAPIKeyAuthCacheInvalidator,
|
||
NewGroupService,
|
||
NewAccountService,
|
||
NewProxyService,
|
||
NewRedeemService,
|
||
NewPromoService,
|
||
NewUsageService,
|
||
NewDashboardService,
|
||
ProvidePricingService,
|
||
NewBillingService,
|
||
NewBillingCacheService,
|
||
NewAnnouncementService,
|
||
NewAdminService,
|
||
NewGatewayService,
|
||
NewOpenAIGatewayService,
|
||
NewOAuthService,
|
||
NewOpenAIOAuthService,
|
||
NewGeminiOAuthService,
|
||
NewGeminiQuotaService,
|
||
NewCompositeTokenCacheInvalidator,
|
||
wire.Bind(new(TokenCacheInvalidator), new(*CompositeTokenCacheInvalidator)),
|
||
NewAntigravityOAuthService,
|
||
NewOAuthRefreshAPI,
|
||
ProvideGeminiTokenProvider,
|
||
NewGeminiMessagesCompatService,
|
||
ProvideAntigravityTokenProvider,
|
||
ProvideOpenAITokenProvider,
|
||
ProvideClaudeTokenProvider,
|
||
NewAntigravityGatewayService,
|
||
ProvideRateLimitService,
|
||
NewAccountUsageService,
|
||
NewAccountTestService,
|
||
ProvideSettingService,
|
||
NewDataManagementService,
|
||
ProvideBackupService,
|
||
ProvideOpsSystemLogSink,
|
||
NewOpsService,
|
||
ProvideOpsMetricsCollector,
|
||
ProvideOpsAggregationService,
|
||
ProvideOpsAlertEvaluatorService,
|
||
ProvideOpsCleanupService,
|
||
ProvideOpsScheduledReportService,
|
||
NewEmailService,
|
||
ProvideEmailQueueService,
|
||
NewTurnstileService,
|
||
NewSubscriptionService,
|
||
wire.Bind(new(DefaultSubscriptionAssigner), new(*SubscriptionService)),
|
||
ProvideConcurrencyService,
|
||
ProvideUserMessageQueueService,
|
||
NewUsageRecordWorkerPool,
|
||
ProvideSchedulerSnapshotService,
|
||
NewIdentityService,
|
||
NewCRSSyncService,
|
||
ProvideUpdateService,
|
||
ProvideTokenRefreshService,
|
||
ProvideAccountExpiryService,
|
||
ProvideSubscriptionExpiryService,
|
||
ProvideTimingWheelService,
|
||
ProvideDashboardAggregationService,
|
||
ProvideUsageCleanupService,
|
||
ProvideDeferredService,
|
||
NewAntigravityQuotaFetcher,
|
||
NewUserAttributeService,
|
||
NewUsageCache,
|
||
NewTotpService,
|
||
NewErrorPassthroughService,
|
||
NewTLSFingerprintProfileService,
|
||
NewDigestSessionStore,
|
||
ProvideIdempotencyCoordinator,
|
||
ProvideSystemOperationLockService,
|
||
ProvideIdempotencyCleanupService,
|
||
ProvideScheduledTestService,
|
||
ProvideScheduledTestRunnerService,
|
||
NewGroupCapacityService,
|
||
NewChannelService,
|
||
NewModelPricingResolver,
|
||
ProvidePaymentConfigService,
|
||
NewPaymentService,
|
||
ProvidePaymentOrderExpiryService,
|
||
ProvideBalanceNotifyService,
|
||
ProvideChannelMonitorService,
|
||
ProvideChannelMonitorRunner,
|
||
NewChannelMonitorRequestTemplateService,
|
||
)
|
||
|
||
// ProvidePaymentConfigService wraps NewPaymentConfigService to accept the named
|
||
// payment.EncryptionKey type instead of raw []byte, avoiding Wire ambiguity.
|
||
func ProvidePaymentConfigService(entClient *dbent.Client, settingRepo SettingRepository, key payment.EncryptionKey) *PaymentConfigService {
|
||
return NewPaymentConfigService(entClient, settingRepo, []byte(key))
|
||
}
|
||
|
||
// ProvideBalanceNotifyService creates BalanceNotifyService
|
||
func ProvideBalanceNotifyService(emailService *EmailService, settingRepo SettingRepository, accountRepo AccountRepository) *BalanceNotifyService {
|
||
return NewBalanceNotifyService(emailService, settingRepo, accountRepo)
|
||
}
|
||
|
||
// ProvidePaymentOrderExpiryService creates and starts PaymentOrderExpiryService.
|
||
func ProvidePaymentOrderExpiryService(paymentSvc *PaymentService) *PaymentOrderExpiryService {
|
||
svc := NewPaymentOrderExpiryService(paymentSvc, 60*time.Second)
|
||
svc.Start()
|
||
return svc
|
||
}
|
||
|
||
// ProvideChannelMonitorService 创建渠道监控服务(CRUD + RunCheck + 用户视图聚合)。
|
||
// 加密器复用 wire 中已注入的 SecretEncryptor(AES-256-GCM)。
|
||
func ProvideChannelMonitorService(
|
||
repo ChannelMonitorRepository,
|
||
encryptor SecretEncryptor,
|
||
) *ChannelMonitorService {
|
||
return NewChannelMonitorService(repo, encryptor)
|
||
}
|
||
|
||
// ProvideChannelMonitorRunner 创建并启动渠道监控调度器。
|
||
// Runner.Stop 由 cleanup function 调用。
|
||
// settingService 用于 runner 每个 tick 读取功能开关。
|
||
func ProvideChannelMonitorRunner(svc *ChannelMonitorService, settingService *SettingService) *ChannelMonitorRunner {
|
||
r := NewChannelMonitorRunner(svc, settingService)
|
||
r.Start()
|
||
return r
|
||
}
|