wire.go 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073
  1. package service
  2. import (
  3. "context"
  4. "database/sql"
  5. "os"
  6. "time"
  7. dbent "github.com/Wei-Shaw/sub2api/ent"
  8. "github.com/Wei-Shaw/sub2api/internal/config"
  9. "github.com/Wei-Shaw/sub2api/internal/payment"
  10. "github.com/Wei-Shaw/sub2api/internal/pkg/antigravity"
  11. "github.com/Wei-Shaw/sub2api/internal/pkg/logger"
  12. "github.com/Wei-Shaw/sub2api/internal/pkg/xai"
  13. "github.com/google/wire"
  14. "github.com/redis/go-redis/v9"
  15. "go.uber.org/zap"
  16. )
  17. func ProvideGrokOAuthService(proxyRepo ProxyRepository, oauthClient GrokOAuthClient, cfg *config.Config, redisClient *redis.Client) *GrokOAuthService {
  18. svc := NewGrokOAuthService(proxyRepo, oauthClient, cfg)
  19. // wire.go is depguard-exempt for redis; construct the Redis session store here.
  20. if redisClient != nil {
  21. svc = svc.WithSessionStore(xai.NewRedisSessionStore(redisClient))
  22. }
  23. return svc
  24. }
  25. // BuildInfo contains build information
  26. type BuildInfo struct {
  27. Version string
  28. BuildType string
  29. }
  30. // ProvidePricingService creates and initializes PricingService
  31. func ProvidePricingService(cfg *config.Config, remoteClient PricingRemoteClient) (*PricingService, error) {
  32. svc := NewPricingService(cfg, remoteClient)
  33. if err := svc.Initialize(); err != nil {
  34. // Pricing service initialization failure should not block startup, use fallback prices
  35. println("[Service] Warning: Pricing service initialization failed:", err.Error())
  36. }
  37. return svc, nil
  38. }
  39. // ProvideUpdateService creates UpdateService with BuildInfo
  40. func ProvideUpdateService(cache UpdateCache, githubClient GitHubReleaseClient, buildInfo BuildInfo) *UpdateService {
  41. return NewUpdateService(cache, githubClient, buildInfo.Version, buildInfo.BuildType)
  42. }
  43. // ProvideEmailQueueService creates EmailQueueService with default worker count
  44. func ProvideEmailQueueService(emailService *EmailService) *EmailQueueService {
  45. return NewEmailQueueService(emailService, 3)
  46. }
  47. // ProvideAuthService wires the optional captcha providers into AuthService while
  48. // keeping NewAuthService's public constructor compatible with existing tests.
  49. func ProvideAuthService(
  50. entClient *dbent.Client,
  51. userRepo UserRepository,
  52. redeemRepo RedeemCodeRepository,
  53. refreshTokenCache RefreshTokenCache,
  54. cfg *config.Config,
  55. settingService *SettingService,
  56. emailService *EmailService,
  57. turnstileService *TurnstileService,
  58. tencentCaptchaService *TencentCaptchaService,
  59. aliyunCaptchaService *AliyunCaptchaService,
  60. emailQueueService *EmailQueueService,
  61. promoService *PromoService,
  62. defaultSubAssigner DefaultSubscriptionAssigner,
  63. affiliateService *AffiliateService,
  64. userPlatformQuotaRepo UserPlatformQuotaRepository,
  65. ) *AuthService {
  66. svc := NewAuthService(
  67. entClient,
  68. userRepo,
  69. redeemRepo,
  70. refreshTokenCache,
  71. cfg,
  72. settingService,
  73. emailService,
  74. turnstileService,
  75. emailQueueService,
  76. promoService,
  77. defaultSubAssigner,
  78. affiliateService,
  79. userPlatformQuotaRepo,
  80. )
  81. svc.SetTencentCaptchaService(tencentCaptchaService)
  82. svc.SetAliyunCaptchaService(aliyunCaptchaService)
  83. return svc
  84. }
  85. // ProvideOAuthRefreshAPI creates OAuthRefreshAPI with the default lock TTL.
  86. func ProvideOAuthRefreshAPI(accountRepo AccountRepository, tokenCache GeminiTokenCache) *OAuthRefreshAPI {
  87. return NewOAuthRefreshAPI(accountRepo, tokenCache)
  88. }
  89. func ProvideBatchImageModelPricingResolver(resolver *ModelPricingResolver) *BatchImageModelPricingResolver {
  90. return &BatchImageModelPricingResolver{Resolver: resolver}
  91. }
  92. func ProvideBatchImageCleanupService(repo BatchImageRepository, accountRepo AccountRepository, cfg *config.Config) *BatchImageCleanupService {
  93. svc := NewBatchImageCleanupService(repo, accountRepo, cfg)
  94. svc.Start()
  95. return svc
  96. }
  97. // ProvideOpenAIOAuthService creates OpenAIOAuthService with privacy/account enrichment support.
  98. func ProvideOpenAIOAuthService(
  99. proxyRepo ProxyRepository,
  100. oauthClient OpenAIOAuthClient,
  101. privacyClientFactory PrivacyClientFactory,
  102. ) *OpenAIOAuthService {
  103. svc := NewOpenAIOAuthService(proxyRepo, oauthClient)
  104. svc.SetPrivacyClientFactory(privacyClientFactory)
  105. return svc
  106. }
  107. // ProvideTokenRefreshService creates and starts TokenRefreshService
  108. func ProvideTokenRefreshService(
  109. accountRepo AccountRepository,
  110. oauthService *OAuthService,
  111. openaiOAuthService *OpenAIOAuthService,
  112. geminiOAuthService *GeminiOAuthService,
  113. antigravityOAuthService *AntigravityOAuthService,
  114. grokOAuthService *GrokOAuthService,
  115. cacheInvalidator TokenCacheInvalidator,
  116. schedulerCache SchedulerCache,
  117. cfg *config.Config,
  118. tempUnschedCache TempUnschedCache,
  119. privacyClientFactory PrivacyClientFactory,
  120. proxyRepo ProxyRepository,
  121. refreshAPI *OAuthRefreshAPI,
  122. runtimeBlocker AccountRuntimeBlocker,
  123. ) *TokenRefreshService {
  124. svc := NewTokenRefreshService(accountRepo, oauthService, openaiOAuthService, geminiOAuthService, antigravityOAuthService, cacheInvalidator, schedulerCache, cfg, tempUnschedCache, grokOAuthService)
  125. // 注入 OpenAI privacy opt-out 依赖
  126. svc.SetPrivacyDeps(privacyClientFactory, proxyRepo)
  127. // 注入统一 OAuth 刷新 API(消除 TokenRefreshService 与 TokenProvider 之间的竞争条件)
  128. svc.SetRefreshAPI(refreshAPI)
  129. // 调用侧显式注入后台刷新策略,避免策略漂移
  130. svc.SetRefreshPolicy(DefaultBackgroundRefreshPolicy())
  131. svc.SetAccountRuntimeBlocker(runtimeBlocker)
  132. svc.Start()
  133. return svc
  134. }
  135. // ProvideClaudeTokenProvider creates ClaudeTokenProvider with OAuthRefreshAPI injection
  136. func ProvideClaudeTokenProvider(
  137. accountRepo AccountRepository,
  138. tokenCache GeminiTokenCache,
  139. oauthService *OAuthService,
  140. refreshAPI *OAuthRefreshAPI,
  141. ) *ClaudeTokenProvider {
  142. p := NewClaudeTokenProvider(accountRepo, tokenCache, oauthService)
  143. executor := NewClaudeTokenRefresher(oauthService)
  144. p.SetRefreshAPI(refreshAPI, executor)
  145. p.SetRefreshPolicy(ClaudeProviderRefreshPolicy())
  146. return p
  147. }
  148. // ProvideOpenAITokenProvider creates OpenAITokenProvider with OAuthRefreshAPI injection
  149. func ProvideOpenAITokenProvider(
  150. accountRepo AccountRepository,
  151. tokenCache GeminiTokenCache,
  152. openaiOAuthService *OpenAIOAuthService,
  153. refreshAPI *OAuthRefreshAPI,
  154. ) *OpenAITokenProvider {
  155. p := NewOpenAITokenProvider(accountRepo, tokenCache, openaiOAuthService)
  156. executor := NewOpenAITokenRefresher(openaiOAuthService, accountRepo)
  157. p.SetRefreshAPI(refreshAPI, executor)
  158. p.SetRefreshPolicy(OpenAIProviderRefreshPolicy())
  159. return p
  160. }
  161. // ProvideOpenAIQuotaService wires the OpenAI quota query/reset service.
  162. // It depends on the OpenAI token provider for refreshed access tokens and the
  163. // privacy client factory for the impersonated upstream HTTP client.
  164. func ProvideOpenAIQuotaService(
  165. accountRepo AccountRepository,
  166. proxyRepo ProxyRepository,
  167. tokenProvider *OpenAITokenProvider,
  168. privacyClientFactory PrivacyClientFactory,
  169. openAIGatewayService *OpenAIGatewayService,
  170. ) *OpenAIQuotaService {
  171. service := NewOpenAIQuotaService(accountRepo, proxyRepo, tokenProvider, privacyClientFactory)
  172. service.agentIdentityWS = openAIGatewayService
  173. return service
  174. }
  175. // ProvideOpenAIQuotaAutoResetService 启动账号级自动用卡队列与补偿扫描。
  176. func ProvideOpenAIQuotaAutoResetService(
  177. accountRepo AccountRepository,
  178. quotaService *OpenAIQuotaService,
  179. rateLimitService *RateLimitService,
  180. idempotency *IdempotencyCoordinator,
  181. audit *AuditLogService,
  182. settingService *SettingService,
  183. leaderLock LeaderLockCache,
  184. ) *OpenAIQuotaAutoResetService {
  185. service := NewOpenAIQuotaAutoResetService(
  186. accountRepo,
  187. quotaService,
  188. rateLimitService,
  189. idempotency,
  190. audit,
  191. settingService,
  192. leaderLock,
  193. )
  194. service.Start()
  195. return service
  196. }
  197. func ProvideAccountUsageService(
  198. accountRepo AccountRepository,
  199. usageLogRepo UsageLogRepository,
  200. usageFetcher ClaudeUsageFetcher,
  201. geminiQuotaService *GeminiQuotaService,
  202. antigravityQuotaFetcher *AntigravityQuotaFetcher,
  203. grokQuotaFetcher *GrokQuotaFetcher,
  204. grokQuotaService *GrokQuotaService,
  205. openAIQuotaService *OpenAIQuotaService,
  206. cache *UsageCache,
  207. identityCache IdentityCache,
  208. tlsFPProfileService *TLSFingerprintProfileService,
  209. openAIGatewayService *OpenAIGatewayService,
  210. ) *AccountUsageService {
  211. service := NewAccountUsageService(
  212. accountRepo,
  213. usageLogRepo,
  214. usageFetcher,
  215. geminiQuotaService,
  216. antigravityQuotaFetcher,
  217. grokQuotaFetcher,
  218. grokQuotaService,
  219. openAIQuotaService,
  220. cache,
  221. identityCache,
  222. tlsFPProfileService,
  223. )
  224. service.agentIdentityWS = openAIGatewayService
  225. return service
  226. }
  227. func ProvideAccountTestService(
  228. concurrencyService *ConcurrencyService,
  229. accountRepo AccountRepository,
  230. geminiTokenProvider *GeminiTokenProvider,
  231. claudeTokenProvider *ClaudeTokenProvider,
  232. grokTokenProvider *GrokTokenProvider,
  233. antigravityGatewayService *AntigravityGatewayService,
  234. httpUpstream HTTPUpstream,
  235. cfg *config.Config,
  236. tlsFPProfileService *TLSFingerprintProfileService,
  237. openAIGatewayService *OpenAIGatewayService,
  238. settingService *SettingService,
  239. pluginManager *PluginManager,
  240. ) *AccountTestService {
  241. service := NewAccountTestService(
  242. accountRepo,
  243. geminiTokenProvider,
  244. claudeTokenProvider,
  245. grokTokenProvider,
  246. antigravityGatewayService,
  247. httpUpstream,
  248. cfg,
  249. tlsFPProfileService,
  250. )
  251. service.concurrencyService = concurrencyService
  252. service.agentIdentityWS = openAIGatewayService
  253. service.SetOpenAIGatewayService(openAIGatewayService)
  254. service.SetSettingService(settingService)
  255. service.SetPluginManager(pluginManager)
  256. return service
  257. }
  258. func ProvideGrokQuotaService(
  259. accountRepo AccountRepository,
  260. proxyRepo ProxyRepository,
  261. tokenProvider *GrokTokenProvider,
  262. httpUpstream HTTPUpstream,
  263. cfg *config.Config,
  264. usageLogRepo UsageLogRepository,
  265. settingService *SettingService,
  266. ) *GrokQuotaService {
  267. service := NewGrokQuotaService(accountRepo, proxyRepo, tokenProvider, httpUpstream, cfg, usageLogRepo)
  268. service.SetSettingService(settingService)
  269. return service
  270. }
  271. // ProvideCNProviderQuotaService 构造国产供应商 Coding Plan 额度探测服务。
  272. func ProvideCNProviderQuotaService(
  273. accountRepo AccountRepository,
  274. proxyRepo ProxyRepository,
  275. httpUpstream HTTPUpstream,
  276. cfg *config.Config,
  277. ) *CNProviderQuotaService {
  278. return NewCNProviderQuotaService(accountRepo, proxyRepo, httpUpstream, cfg)
  279. }
  280. // ProvideCNProviderBalanceService 构造国产供应商余额探测服务。
  281. func ProvideCNProviderBalanceService(
  282. accountRepo AccountRepository,
  283. proxyRepo ProxyRepository,
  284. httpUpstream HTTPUpstream,
  285. cfg *config.Config,
  286. ) *CNProviderBalanceService {
  287. return NewCNProviderBalanceService(accountRepo, proxyRepo, httpUpstream, cfg)
  288. }
  289. // ProvideCNProviderBalanceCheckService 构造并启动周期余额/额度检测任务。
  290. // payg 账号探余额(低余额停调);coding plan 账号探 5h/weekly 滚动窗口
  291. // (落 extra 快照供调度阈值评估自动停调)。
  292. // 间隔取自 gateway.cn_providers.balance_check_interval_minutes;<=0 或关闭时不启动。
  293. func ProvideCNProviderBalanceCheckService(
  294. accountRepo AccountRepository,
  295. balanceService *CNProviderBalanceService,
  296. quotaService *CNProviderQuotaService,
  297. cfg *config.Config,
  298. ) *CNProviderBalanceCheckService {
  299. minutes := 10
  300. if cfg != nil && cfg.Gateway.CNProviders.BalanceCheckIntervalMinutes > 0 {
  301. minutes = cfg.Gateway.CNProviders.BalanceCheckIntervalMinutes
  302. }
  303. svc := NewCNProviderBalanceCheckService(accountRepo, balanceService, quotaService, cfg, time.Duration(minutes)*time.Minute)
  304. svc.Start()
  305. return svc
  306. }
  307. // ProvideGeminiTokenProvider creates GeminiTokenProvider with OAuthRefreshAPI injection
  308. func ProvideGeminiTokenProvider(
  309. accountRepo AccountRepository,
  310. tokenCache GeminiTokenCache,
  311. geminiOAuthService *GeminiOAuthService,
  312. refreshAPI *OAuthRefreshAPI,
  313. ) *GeminiTokenProvider {
  314. p := NewGeminiTokenProvider(accountRepo, tokenCache, geminiOAuthService)
  315. executor := NewGeminiTokenRefresher(geminiOAuthService)
  316. p.SetRefreshAPI(refreshAPI, executor)
  317. p.SetRefreshPolicy(GeminiProviderRefreshPolicy())
  318. return p
  319. }
  320. // ProvideAntigravityTokenProvider creates AntigravityTokenProvider with OAuthRefreshAPI injection
  321. func ProvideAntigravityTokenProvider(
  322. accountRepo AccountRepository,
  323. tokenCache GeminiTokenCache,
  324. antigravityOAuthService *AntigravityOAuthService,
  325. refreshAPI *OAuthRefreshAPI,
  326. tempUnschedCache TempUnschedCache,
  327. ) *AntigravityTokenProvider {
  328. p := NewAntigravityTokenProvider(accountRepo, tokenCache, antigravityOAuthService)
  329. executor := NewAntigravityTokenRefresher(antigravityOAuthService)
  330. p.SetRefreshAPI(refreshAPI, executor)
  331. p.SetRefreshPolicy(AntigravityProviderRefreshPolicy())
  332. p.SetTempUnschedCache(tempUnschedCache)
  333. return p
  334. }
  335. // ProvideGrokTokenProvider creates GrokTokenProvider with OAuthRefreshAPI injection.
  336. func ProvideGrokTokenProvider(
  337. accountRepo AccountRepository,
  338. tokenCache GeminiTokenCache,
  339. grokOAuthService *GrokOAuthService,
  340. refreshAPI *OAuthRefreshAPI,
  341. tempUnschedCache TempUnschedCache,
  342. ) *GrokTokenProvider {
  343. p := NewGrokTokenProvider(accountRepo, tokenCache)
  344. executor := NewGrokTokenRefresher(grokOAuthService)
  345. p.SetRefreshAPI(refreshAPI, executor)
  346. p.SetRefreshPolicy(GrokProviderRefreshPolicy())
  347. p.SetTempUnschedCache(tempUnschedCache)
  348. return p
  349. }
  350. // ProvideDashboardAggregationService 创建并启动仪表盘聚合服务
  351. func ProvideDashboardAggregationService(repo DashboardAggregationRepository, timingWheel *TimingWheelService, lockCache LeaderLockCache, db *sql.DB, cfg *config.Config) *DashboardAggregationService {
  352. svc := NewDashboardAggregationService(repo, timingWheel, cfg)
  353. svc.SetLeaderLock(lockCache, db)
  354. svc.Start()
  355. return svc
  356. }
  357. // ProvideUsageCleanupService 创建并启动使用记录清理任务服务
  358. func ProvideUsageCleanupService(repo UsageCleanupRepository, timingWheel *TimingWheelService, dashboardAgg *DashboardAggregationService, cfg *config.Config) *UsageCleanupService {
  359. svc := NewUsageCleanupService(repo, timingWheel, dashboardAgg, cfg)
  360. svc.Start()
  361. return svc
  362. }
  363. // ProvideAccountExpiryService creates and starts AccountExpiryService.
  364. func ProvideAccountExpiryService(accountRepo AccountRepository) *AccountExpiryService {
  365. svc := NewAccountExpiryService(accountRepo, time.Minute)
  366. svc.Start()
  367. return svc
  368. }
  369. // ProvideOpenAICodexVersionSyncService creates and starts OpenAICodexVersionSyncService.
  370. // 出站 Codex 身份的版本号靠它跟随官方发布,无需为了跟版本而发新版本;面板可关闭。
  371. func ProvideOpenAICodexVersionSyncService(
  372. settingRepo SettingRepository,
  373. settingService *SettingService,
  374. githubClient GitHubReleaseClient,
  375. ) *OpenAICodexVersionSyncService {
  376. svc := NewOpenAICodexVersionSyncService(settingRepo, settingService, githubClient, openAICodexVersionSyncInterval)
  377. svc.Start()
  378. return svc
  379. }
  380. // ProvideProxyExpiryService creates and starts ProxyExpiryService.
  381. func ProvideProxyExpiryService(proxyRepo ProxyRepository) *ProxyExpiryService {
  382. svc := NewProxyExpiryService(proxyRepo, time.Minute)
  383. svc.Start()
  384. return svc
  385. }
  386. // ProvideSubscriptionExpiryService creates and starts SubscriptionExpiryService.
  387. func ProvideSubscriptionExpiryService(userSubRepo UserSubscriptionRepository, settingRepo SettingRepository, notificationEmailService *NotificationEmailService, lockCache LeaderLockCache, db *sql.DB) *SubscriptionExpiryService {
  388. svc := NewSubscriptionExpiryService(userSubRepo, time.Minute)
  389. svc.SetSettingRepository(settingRepo)
  390. svc.SetNotificationEmailService(notificationEmailService)
  391. svc.SetLeaderLock(lockCache, db)
  392. svc.Start()
  393. return svc
  394. }
  395. // ProvideTimingWheelService creates and starts TimingWheelService
  396. func ProvideTimingWheelService() (*TimingWheelService, error) {
  397. svc, err := NewTimingWheelService()
  398. if err != nil {
  399. return nil, err
  400. }
  401. svc.Start()
  402. return svc, nil
  403. }
  404. // ProvideDeferredService creates and starts DeferredService
  405. func ProvideDeferredService(accountRepo AccountRepository, timingWheel *TimingWheelService) *DeferredService {
  406. svc := NewDeferredService(accountRepo, timingWheel, 10*time.Second)
  407. svc.Start()
  408. return svc
  409. }
  410. // ProvideConcurrencyService creates ConcurrencyService and starts slot cleanup worker.
  411. func ProvideConcurrencyService(cache ConcurrencyCache, accountRepo AccountRepository, cfg *config.Config) *ConcurrencyService {
  412. svc := NewConcurrencyService(cache)
  413. if err := svc.CleanupStaleProcessSlots(context.Background()); err != nil {
  414. logger.LegacyPrintf("service.concurrency", "Warning: startup cleanup stale process slots failed: %v", err)
  415. }
  416. if cfg != nil {
  417. svc.SetAccountLoadBatchCacheTTL(time.Duration(cfg.Gateway.Scheduling.LoadBatchCacheTTLMS) * time.Millisecond)
  418. svc.StartSlotCleanupWorker(accountRepo, cfg.Gateway.Scheduling.SlotCleanupInterval)
  419. }
  420. return svc
  421. }
  422. // ProvideUserMessageQueueService 创建用户消息串行队列服务并启动清理 worker
  423. func ProvideUserMessageQueueService(cache UserMsgQueueCache, rpmCache RPMCache, cfg *config.Config) *UserMessageQueueService {
  424. svc := NewUserMessageQueueService(cache, rpmCache, &cfg.Gateway.UserMessageQueue)
  425. if cfg.Gateway.UserMessageQueue.CleanupIntervalSeconds > 0 {
  426. svc.StartCleanupWorker(time.Duration(cfg.Gateway.UserMessageQueue.CleanupIntervalSeconds) * time.Second)
  427. }
  428. return svc
  429. }
  430. // ProvideSchedulerSnapshotService creates and starts SchedulerSnapshotService.
  431. func ProvideSchedulerSnapshotService(
  432. cache SchedulerCache,
  433. outboxRepo SchedulerOutboxRepository,
  434. accountRepo AccountRepository,
  435. groupRepo GroupRepository,
  436. cfg *config.Config,
  437. ) *SchedulerSnapshotService {
  438. svc := NewSchedulerSnapshotService(cache, outboxRepo, accountRepo, groupRepo, cfg)
  439. svc.Start()
  440. return svc
  441. }
  442. // ProvideRateLimitService creates RateLimitService with optional dependencies.
  443. func ProvideRateLimitService(
  444. accountRepo AccountRepository,
  445. usageRepo UsageLogRepository,
  446. cfg *config.Config,
  447. geminiQuotaService *GeminiQuotaService,
  448. tempUnschedCache TempUnschedCache,
  449. timeoutCounterCache TimeoutCounterCache,
  450. openAI403CounterCache OpenAI403CounterCache,
  451. settingService *SettingService,
  452. tokenCacheInvalidator TokenCacheInvalidator,
  453. ) *RateLimitService {
  454. svc := NewRateLimitService(accountRepo, usageRepo, cfg, geminiQuotaService, tempUnschedCache)
  455. if healthCache, ok := tempUnschedCache.(OpenAIAPIKeyHealthCache); ok {
  456. svc.SetOpenAIAPIKeyHealthCache(healthCache)
  457. }
  458. svc.SetTimeoutCounterCache(timeoutCounterCache)
  459. svc.SetOpenAI403CounterCache(openAI403CounterCache)
  460. svc.SetSettingService(settingService)
  461. svc.SetTokenCacheInvalidator(tokenCacheInvalidator)
  462. return svc
  463. }
  464. // ProvideOpsMetricsCollector creates and starts OpsMetricsCollector.
  465. func ProvideOpsMetricsCollector(
  466. opsRepo OpsRepository,
  467. settingRepo SettingRepository,
  468. accountRepo AccountRepository,
  469. concurrencyService *ConcurrencyService,
  470. db *sql.DB,
  471. redisClient *redis.Client,
  472. cfg *config.Config,
  473. ) *OpsMetricsCollector {
  474. collector := NewOpsMetricsCollector(opsRepo, settingRepo, accountRepo, concurrencyService, db, redisClient, cfg)
  475. collector.Start()
  476. return collector
  477. }
  478. // ProvideOpsAggregationService creates and starts OpsAggregationService (hourly/daily pre-aggregation).
  479. func ProvideOpsAggregationService(
  480. opsRepo OpsRepository,
  481. settingRepo SettingRepository,
  482. db *sql.DB,
  483. redisClient *redis.Client,
  484. cfg *config.Config,
  485. ) *OpsAggregationService {
  486. svc := NewOpsAggregationService(opsRepo, settingRepo, db, redisClient, cfg)
  487. svc.Start()
  488. return svc
  489. }
  490. // ProvideOpsAlertEvaluatorService creates and starts OpsAlertEvaluatorService.
  491. func ProvideOpsAlertEvaluatorService(
  492. opsService *OpsService,
  493. opsRepo OpsRepository,
  494. emailService *EmailService,
  495. redisClient *redis.Client,
  496. cfg *config.Config,
  497. proxyRepo ProxyRepository,
  498. ) *OpsAlertEvaluatorService {
  499. svc := NewOpsAlertEvaluatorService(opsService, opsRepo, emailService, redisClient, cfg, proxyRepo)
  500. svc.Start()
  501. return svc
  502. }
  503. // ProvideOpsCleanupService creates and starts OpsCleanupService (cron scheduled).
  504. // channelMonitorSvc 让维护任务(聚合 + 历史/聚合软删)跟随 ops 清理 cron 一起跑,
  505. // 共享 leader lock + heartbeat。
  506. // settingRepo 让 cleanup service 自己读 ops_advanced_settings.data_retention 覆盖 cfg;
  507. // opsService 用来反向注入 cleanup hook,以便 UI 改清理设置时能 Reload cron。
  508. func ProvideOpsCleanupService(
  509. opsRepo OpsRepository,
  510. db *sql.DB,
  511. redisClient *redis.Client,
  512. cfg *config.Config,
  513. channelMonitorSvc *ChannelMonitorService,
  514. settingRepo SettingRepository,
  515. opsService *OpsService,
  516. ) *OpsCleanupService {
  517. svc := NewOpsCleanupService(opsRepo, db, redisClient, cfg, channelMonitorSvc, settingRepo)
  518. svc.Start()
  519. if opsService != nil {
  520. opsService.SetCleanupReloader(svc)
  521. }
  522. return svc
  523. }
  524. func ProvideOpsSystemLogSink(opsRepo OpsRepository) *OpsSystemLogSink {
  525. sink := NewOpsSystemLogSink(opsRepo)
  526. sink.Start()
  527. logger.SetSink(sink)
  528. return sink
  529. }
  530. // ProvideAuditLogService 创建操作审计日志服务并启动异步写入与保留期清理协程。
  531. // 停止逻辑挂在 cmd/server 的 provideCleanup。
  532. func ProvideAuditLogService(repo AuditLogRepository, settingService *SettingService) *AuditLogService {
  533. svc := NewAuditLogService(repo, settingService)
  534. svc.Start()
  535. return svc
  536. }
  537. func buildIdempotencyConfig(cfg *config.Config) IdempotencyConfig {
  538. idempotencyCfg := DefaultIdempotencyConfig()
  539. if cfg != nil {
  540. if cfg.Idempotency.DefaultTTLSeconds > 0 {
  541. idempotencyCfg.DefaultTTL = time.Duration(cfg.Idempotency.DefaultTTLSeconds) * time.Second
  542. }
  543. if cfg.Idempotency.SystemOperationTTLSeconds > 0 {
  544. idempotencyCfg.SystemOperationTTL = time.Duration(cfg.Idempotency.SystemOperationTTLSeconds) * time.Second
  545. }
  546. if cfg.Idempotency.ProcessingTimeoutSeconds > 0 {
  547. idempotencyCfg.ProcessingTimeout = time.Duration(cfg.Idempotency.ProcessingTimeoutSeconds) * time.Second
  548. }
  549. if cfg.Idempotency.FailedRetryBackoffSeconds > 0 {
  550. idempotencyCfg.FailedRetryBackoff = time.Duration(cfg.Idempotency.FailedRetryBackoffSeconds) * time.Second
  551. }
  552. if cfg.Idempotency.MaxStoredResponseLen > 0 {
  553. idempotencyCfg.MaxStoredResponseLen = cfg.Idempotency.MaxStoredResponseLen
  554. }
  555. idempotencyCfg.ObserveOnly = cfg.Idempotency.ObserveOnly
  556. }
  557. return idempotencyCfg
  558. }
  559. func ProvideIdempotencyCoordinator(repo IdempotencyRepository, cfg *config.Config) *IdempotencyCoordinator {
  560. coordinator := NewIdempotencyCoordinator(repo, buildIdempotencyConfig(cfg))
  561. SetDefaultIdempotencyCoordinator(coordinator)
  562. return coordinator
  563. }
  564. func ProvideSystemOperationLockService(repo IdempotencyRepository, cfg *config.Config) *SystemOperationLockService {
  565. return NewSystemOperationLockService(repo, buildIdempotencyConfig(cfg))
  566. }
  567. func ProvideIdempotencyCleanupService(repo IdempotencyRepository, cfg *config.Config) *IdempotencyCleanupService {
  568. svc := NewIdempotencyCleanupService(repo, cfg)
  569. svc.Start()
  570. return svc
  571. }
  572. // ProvideScheduledTestService creates ScheduledTestService.
  573. func ProvideScheduledTestService(
  574. planRepo ScheduledTestPlanRepository,
  575. resultRepo ScheduledTestResultRepository,
  576. ) *ScheduledTestService {
  577. return NewScheduledTestService(planRepo, resultRepo)
  578. }
  579. // ProvideScheduledTestRunnerService creates and starts ScheduledTestRunnerService.
  580. func ProvideScheduledTestRunnerService(
  581. planRepo ScheduledTestPlanRepository,
  582. scheduledSvc *ScheduledTestService,
  583. accountTestSvc *AccountTestService,
  584. rateLimitSvc *RateLimitService,
  585. cfg *config.Config,
  586. ) *ScheduledTestRunnerService {
  587. svc := NewScheduledTestRunnerService(planRepo, scheduledSvc, accountTestSvc, rateLimitSvc, cfg)
  588. svc.Start()
  589. return svc
  590. }
  591. // ProvideOpsScheduledReportService creates and starts OpsScheduledReportService.
  592. func ProvideOpsScheduledReportService(
  593. opsService *OpsService,
  594. userService *UserService,
  595. emailService *EmailService,
  596. redisClient *redis.Client,
  597. cfg *config.Config,
  598. ) *OpsScheduledReportService {
  599. svc := NewOpsScheduledReportService(opsService, userService, emailService, redisClient, cfg)
  600. svc.Start()
  601. return svc
  602. }
  603. // ProvideAPIKeyAuthCacheInvalidator 提供 API Key 认证缓存失效能力
  604. func ProvideAPIKeyAuthCacheInvalidator(apiKeyService *APIKeyService) APIKeyAuthCacheInvalidator {
  605. // Start Pub/Sub subscriber for L1 cache invalidation across instances
  606. apiKeyService.StartAuthCacheInvalidationSubscriber(context.Background())
  607. return apiKeyService
  608. }
  609. // ProvideImageStorageSettingService 构造异步生图对象存储的后台设置服务。
  610. //
  611. // config.yaml 里的 image_storage 作为回落:后台从未保存过设置时沿用它,
  612. // 使升级前已通过配置文件开启该功能的部署不被打断。
  613. func ProvideImageStorageSettingService(
  614. settingRepo SettingRepository,
  615. encryptor SecretEncryptor,
  616. backup *BackupService,
  617. factory ImageStorageFactory,
  618. cfg *config.Config,
  619. ) *ImageStorageSettingService {
  620. if cfg.ImageStorage.Enabled && !cfg.ImageStorage.Active() {
  621. // 列出具体缺失的键。若这些键其实已在环境变量里设过,说明它们没被读进来,
  622. // 请确认 setDefaults 中已为其注册默认值(见 config.setEnvReachableDefaults)。
  623. logger.L().Warn("image_storage.enabled is true in config but object storage is not fully configured; configure it in the admin UI or complete the config file",
  624. zap.Strings("missing_keys", cfg.ImageStorage.MissingCredentialKeys()))
  625. }
  626. return NewImageStorageSettingService(settingRepo, encryptor, backup, factory, cfg.ImageStorage)
  627. }
  628. // ProvideImageTaskService 构造异步图片任务服务。
  629. //
  630. // 对象存储是异步图片任务的启用前提:仅当开关打开且凭证齐全时功能才可用,否则整体禁用
  631. // (handler 返回 404,不创建任务、不写 Redis),从而避免大 base64 结果撑爆 Redis。
  632. // 启用状态由 settings 服务在运行时解析,因此后台改开关后无需重启即可生效。
  633. func ProvideImageTaskService(store ImageTaskStore, settings *ImageStorageSettingService) *ImageTaskService {
  634. return NewImageTaskServiceWithResolver(store, settings.Resolver(), defaultImageTaskTTL, defaultImageTaskExecutionTimeout)
  635. }
  636. // ProvideBackupService creates and starts BackupService
  637. func ProvideBackupService(
  638. settingRepo SettingRepository,
  639. cfg *config.Config,
  640. encryptor SecretEncryptor,
  641. storeFactory BackupObjectStoreFactory,
  642. dumper DBDumper,
  643. lockCache LeaderLockCache,
  644. db *sql.DB,
  645. ) *BackupService {
  646. svc := NewBackupService(settingRepo, cfg, encryptor, storeFactory, dumper)
  647. svc.SetLeaderLock(lockCache, db)
  648. svc.Start()
  649. return svc
  650. }
  651. // ProvideOpsService constructs OpsService and wires the SettingService-backed quota
  652. // auto-pause cache sink. Mirrors the SetCleanupReloader pattern: OpsService doesn't
  653. // hold a *SettingService reference, but wire injects a tiny callback so writes to
  654. // ops_advanced_settings immediately propagate into the scheduler hot-path cache.
  655. func ProvideOpsService(
  656. opsRepo OpsRepository,
  657. settingRepo SettingRepository,
  658. cfg *config.Config,
  659. accountRepo AccountRepository,
  660. userRepo UserRepository,
  661. concurrencyService *ConcurrencyService,
  662. gatewayService *GatewayService,
  663. openAIGatewayService *OpenAIGatewayService,
  664. geminiCompatService *GeminiMessagesCompatService,
  665. antigravityGatewayService *AntigravityGatewayService,
  666. systemLogSink *OpsSystemLogSink,
  667. settingService *SettingService,
  668. authCacheInvalidationWorker *AuthCacheInvalidationWorker,
  669. apiKeyService *APIKeyService,
  670. ) *OpsService {
  671. svc := NewOpsService(
  672. opsRepo,
  673. settingRepo,
  674. cfg,
  675. accountRepo,
  676. userRepo,
  677. concurrencyService,
  678. gatewayService,
  679. openAIGatewayService,
  680. geminiCompatService,
  681. antigravityGatewayService,
  682. systemLogSink,
  683. )
  684. if settingService != nil {
  685. svc.SetOpenAIQuotaAutoPauseSettingsSink(settingService.SetOpenAIQuotaAutoPauseSettings)
  686. // Optional warm-up so the first scheduled request after process start observes
  687. // a populated cache rather than zero defaults. Best-effort, sync-bounded.
  688. settingService.WarmOpenAIQuotaAutoPauseSettings(context.Background())
  689. }
  690. svc.authCacheInvalidationWorker = authCacheInvalidationWorker
  691. svc.apiKeyService = apiKeyService
  692. svc.StartRuntimeSettingsRefresh(context.Background())
  693. return svc
  694. }
  695. // ProvideOpsIngressRejectAggregator starts the bounded security aggregation
  696. // runtime and attaches it to OpsService, which is the middleware recorder.
  697. func ProvideOpsIngressRejectAggregator(opsRepo OpsRepository, opsService *OpsService) *OpsIngressRejectAggregator {
  698. repo, ok := opsRepo.(OpsIngressRejectRepository)
  699. if !ok {
  700. return nil
  701. }
  702. aggregator := NewOpsIngressRejectAggregator(repo)
  703. aggregator.Start()
  704. opsService.SetIngressRejectAggregator(aggregator)
  705. return aggregator
  706. }
  707. // ProvideSettingService wires SettingService with group reader and proxy repo.
  708. func ProvideSettingService(settingRepo SettingRepository, groupRepo GroupRepository, proxyRepo ProxyRepository, cfg *config.Config) *SettingService {
  709. svc := NewSettingService(settingRepo, cfg)
  710. svc.SetDefaultSubscriptionGroupReader(groupRepo)
  711. svc.SetProxyRepository(proxyRepo)
  712. if err := svc.LoadForwardedClientIPSettings(context.Background()); err != nil {
  713. logger.LegacyPrintf("service.setting", "Warning: load forwarded client IP settings failed: %v", err)
  714. }
  715. if err := svc.MigrateOpenAIAllowClaudeCodeCodexPluginSetting(context.Background()); err != nil {
  716. logger.LegacyPrintf("service.setting", "Warning: migrate openai allow Claude Code Codex plugin setting failed: %v", err)
  717. }
  718. if err := svc.MigrateCodexBodyFingerprintToSignals(context.Background()); err != nil {
  719. logger.LegacyPrintf("service.setting", "Warning: migrate codex body fingerprint to signals failed: %v", err)
  720. }
  721. if err := svc.MigrateGrokDefaultTextModel(context.Background()); err != nil {
  722. logger.LegacyPrintf("service.setting", "Warning: migrate Grok default text model failed: %v", err)
  723. }
  724. antigravity.SetUserAgentVersionResolver(svc.GetAntigravityUserAgentVersion)
  725. // enforceCodexIdentityHeaders 是所有 Codex 出站路径共用的纯函数收口点,拿不到 ctx,
  726. // 故注入无参解析器;解析器内部自带 60s TTL 缓存,热路径不触库。
  727. SetCodexCanonicalUserAgentResolver(func() string {
  728. return svc.GetOpenAICodexCanonicalUserAgent(context.Background())
  729. })
  730. return svc
  731. }
  732. // ProvideBillingCacheService wires BillingCacheService with its RPM dependencies.
  733. func ProvideBillingCacheService(
  734. cache BillingCache,
  735. userRepo UserRepository,
  736. subRepo UserSubscriptionRepository,
  737. apiKeyRepo APIKeyRepository,
  738. rpmCache UserRPMCache,
  739. rateRepo UserGroupRateRepository,
  740. cfg *config.Config,
  741. userPlatformQuotaRepo UserPlatformQuotaRepository,
  742. ) *BillingCacheService {
  743. return NewBillingCacheService(cache, userRepo, subRepo, apiKeyRepo, rpmCache, rateRepo, cfg, userPlatformQuotaRepo)
  744. }
  745. // ProvideAPIKeyService wires APIKeyService and connects rate-limit cache invalidation.
  746. func ProvideAPIKeyService(
  747. apiKeyRepo APIKeyRepository,
  748. userRepo UserRepository,
  749. groupRepo GroupRepository,
  750. userSubRepo UserSubscriptionRepository,
  751. userGroupRateRepo UserGroupRateRepository,
  752. cache APIKeyCache,
  753. cfg *config.Config,
  754. billingCacheService *BillingCacheService,
  755. concurrencyService *ConcurrencyService,
  756. ) *APIKeyService {
  757. svc := NewAPIKeyService(apiKeyRepo, userRepo, groupRepo, userSubRepo, userGroupRateRepo, cache, cfg)
  758. svc.SetRateLimitCacheInvalidator(billingCacheService)
  759. svc.SetConcurrencyService(concurrencyService)
  760. return svc
  761. }
  762. func ProvideIntelligentTestService(repo IntelligentTestRepository, runner *AccountTestService) *IntelligentTestService {
  763. svc := NewIntelligentTestService(repo, runner)
  764. svc.Start()
  765. return svc
  766. }
  767. func ProvideTLSFingerprintProfileService(repo TLSFingerprintProfileRepository, cache TLSFingerprintProfileCache, cfg *config.Config) *TLSFingerprintProfileService {
  768. return NewTLSFingerprintProfileService(repo, cache, cfg)
  769. }
  770. // ProviderSet is the Wire provider set for all services
  771. var ProviderSet = wire.NewSet(
  772. NewUserCleanupService,
  773. ProvideIntelligentTestService,
  774. // Core services
  775. ProvideAuthService,
  776. NewPasskeyService,
  777. NewUserService,
  778. ProvideAPIKeyService,
  779. ProvideAPIKeyAuthCacheInvalidator,
  780. ProvideAuthCacheInvalidationWorker,
  781. NewGroupService,
  782. NewCompositeRouteResolver,
  783. NewAccountService,
  784. NewProxyService,
  785. NewRedeemService,
  786. NewPromoService,
  787. NewUsageService,
  788. NewAccountTrafficService,
  789. NewDashboardService,
  790. ProvidePricingService,
  791. NewBillingService,
  792. ProvideBillingCacheService,
  793. NewAnnouncementService,
  794. NewAdminService,
  795. wire.Bind(new(AntiDegradeStore), new(AdminService)),
  796. NewGatewayService,
  797. NewOpenAIGatewayService,
  798. ProvideImageStorageSettingService,
  799. ProvideImageTaskService,
  800. ProvideBatchImageModelPricingResolver,
  801. NewBatchImagePublicService,
  802. NewBatchImageDownloadService,
  803. ProvideBatchImageCleanupService,
  804. ProvideBatchImageWorkerRuntime,
  805. wire.Bind(new(AccountRuntimeBlocker), new(*OpenAIGatewayService)),
  806. NewOAuthService,
  807. ProvideOpenAIOAuthService,
  808. ProvideGrokOAuthService,
  809. wire.Bind(new(GrokOAuthTokenService), new(*GrokOAuthService)),
  810. NewGeminiOAuthService,
  811. NewGeminiQuotaService,
  812. NewCompositeTokenCacheInvalidator,
  813. wire.Bind(new(TokenCacheInvalidator), new(*CompositeTokenCacheInvalidator)),
  814. NewAntigravityOAuthService,
  815. ProvideOAuthRefreshAPI,
  816. ProvideGeminiTokenProvider,
  817. NewGeminiMessagesCompatService,
  818. ProvideAntigravityTokenProvider,
  819. ProvideGrokTokenProvider,
  820. ProvideOpenAITokenProvider,
  821. ProvideOpenAIQuotaService,
  822. ProvideOpenAIQuotaAutoResetService,
  823. ProvideGrokQuotaService,
  824. ProvideCNProviderQuotaService,
  825. ProvideCNProviderBalanceService,
  826. ProvideCNProviderBalanceCheckService,
  827. ProvideClaudeTokenProvider,
  828. NewAntigravityGatewayService,
  829. ProvideRateLimitService,
  830. ProvideAccountUsageService,
  831. ProvideAccountTestService,
  832. ProvideUpstreamBillingProbeService,
  833. ProvideOllamaCloudUsageService,
  834. ProvideSettingService,
  835. NewDataManagementService,
  836. ProvideBackupService,
  837. ProvideOpsSystemLogSink,
  838. ProvideOpsService,
  839. ProvideOpsIngressRejectAggregator,
  840. ProvideAuditLogService,
  841. ProvideOpsMetricsCollector,
  842. ProvideOpsAggregationService,
  843. ProvideOpsAlertEvaluatorService,
  844. ProvideOpsCleanupService,
  845. ProvideOpsScheduledReportService,
  846. NewEmailService,
  847. NewNotificationEmailService,
  848. ProvideEmailQueueService,
  849. NewTurnstileService,
  850. NewTencentCaptchaService,
  851. NewAliyunCaptchaService,
  852. NewSubscriptionService,
  853. wire.Bind(new(DefaultSubscriptionAssigner), new(*SubscriptionService)),
  854. ProvideConcurrencyService,
  855. ProvideUserMessageQueueService,
  856. NewUsageRecordWorkerPool,
  857. ProvideSchedulerSnapshotService,
  858. NewIdentityService,
  859. NewCRSSyncService,
  860. ProvideUpdateService,
  861. ProvideTokenRefreshService,
  862. wire.Bind(new(GrokOAuthReconciler), new(*TokenRefreshService)),
  863. ProvideAccountExpiryService,
  864. ProvideOpenAICodexVersionSyncService,
  865. ProvideProxyExpiryService,
  866. ProvideSubscriptionExpiryService,
  867. ProvideTimingWheelService,
  868. ProvideDashboardAggregationService,
  869. ProvideUsageCleanupService,
  870. ProvideDeferredService,
  871. NewAntigravityQuotaFetcher,
  872. NewGrokQuotaFetcher,
  873. NewUserAttributeService,
  874. NewUsageCache,
  875. NewTotpService,
  876. NewErrorPassthroughService,
  877. ProvideTLSFingerprintProfileService,
  878. NewPluginManager,
  879. NewDigestSessionStore,
  880. ProvideIdempotencyCoordinator,
  881. ProvideSystemOperationLockService,
  882. ProvideIdempotencyCleanupService,
  883. ProvideScheduledTestService,
  884. ProvideScheduledTestRunnerService,
  885. NewGroupCapacityService,
  886. NewChannelService,
  887. wire.Bind(new(ChannelCacheInvalidator), new(*ChannelService)),
  888. NewModelPricingResolverWithGlobal,
  889. NewModelPlazaService,
  890. NewContentModerationService,
  891. wire.Bind(new(SecurityPolicyModelReviewer), new(*ContentModerationService)),
  892. NewSecurityPolicyService,
  893. NewGlobalModelPricingService,
  894. ProvideAccountHealthService,
  895. ProvideMarginService,
  896. NewTieredRoutingService,
  897. ProvideSpendGuardService,
  898. NewTicketService,
  899. NewBillingExportService,
  900. ProvideAntiDegradeService,
  901. NewAffiliateService,
  902. ProvidePaymentConfigService,
  903. ProvidePaymentService,
  904. ProvidePaymentOrderExpiryService,
  905. ProvideBalanceNotifyService,
  906. ProvideChannelMonitorService,
  907. ProvideChannelMonitorRunner,
  908. NewChannelMonitorQuotaFetcher,
  909. ProvideChannelMonitorV2Service,
  910. ProvideChannelMonitorV2Aggregator,
  911. NewChannelMonitorRequestTemplateService,
  912. ProvideUserPlatformQuotaUsageFlusher,
  913. )
  914. func ProvideAntiDegradeService(admin AdminService, cfg *config.Config, plugins *PluginManager) *AntiDegradeService {
  915. svc := NewAntiDegradeService(admin)
  916. svc.cfg, svc.pluginManager = cfg, plugins
  917. return svc
  918. }
  919. // ProvideUserPlatformQuotaUsageFlusher 创建并启动 UserPlatformQuotaUsageFlusher。
  920. func ProvideUserPlatformQuotaUsageFlusher(cfg *config.Config, cache BillingCache, quotaRepo UserPlatformQuotaRepository, tw *TimingWheelService) *UserPlatformQuotaUsageFlusher {
  921. svc := NewUserPlatformQuotaUsageFlusher(cfg, cache, quotaRepo, tw)
  922. svc.Start()
  923. return svc
  924. }
  925. // ProvidePaymentConfigService wraps NewPaymentConfigService to accept the named
  926. // payment.EncryptionKey type instead of raw []byte, avoiding Wire ambiguity.
  927. func ProvidePaymentConfigService(entClient *dbent.Client, settingRepo SettingRepository, key payment.EncryptionKey) *PaymentConfigService {
  928. return NewPaymentConfigService(entClient, settingRepo, []byte(key))
  929. }
  930. // ProvideBalanceNotifyService creates BalanceNotifyService
  931. func ProvideBalanceNotifyService(emailService *EmailService, settingRepo SettingRepository, accountRepo AccountRepository, notificationEmailService *NotificationEmailService) *BalanceNotifyService {
  932. svc := NewBalanceNotifyService(emailService, settingRepo, accountRepo)
  933. svc.SetNotificationEmailService(notificationEmailService)
  934. return svc
  935. }
  936. // ProvidePaymentService creates PaymentService and attaches notification email delivery.
  937. func ProvidePaymentService(entClient *dbent.Client, registry *payment.Registry, loadBalancer payment.LoadBalancer, redeemService *RedeemService, subscriptionSvc *SubscriptionService, configService *PaymentConfigService, userRepo UserRepository, groupRepo GroupRepository, affiliateService *AffiliateService, notificationEmailService *NotificationEmailService) *PaymentService {
  938. svc := NewPaymentService(entClient, registry, loadBalancer, redeemService, subscriptionSvc, configService, userRepo, groupRepo, affiliateService)
  939. svc.SetNotificationEmailService(notificationEmailService)
  940. return svc
  941. }
  942. // ProvidePaymentOrderExpiryService creates and starts PaymentOrderExpiryService.
  943. func ProvidePaymentOrderExpiryService(paymentSvc *PaymentService, lockCache LeaderLockCache, db *sql.DB) *PaymentOrderExpiryService {
  944. svc := NewPaymentOrderExpiryService(paymentSvc, 60*time.Second)
  945. svc.SetLeaderLock(lockCache, db)
  946. svc.Start()
  947. return svc
  948. }
  949. // ProvideChannelMonitorService 创建渠道监控服务(CRUD + RunCheck + 用户视图聚合)。
  950. // 加密器复用 wire 中已注入的 SecretEncryptor(AES-256-GCM)。
  951. // settingService gates RunCheck via channel_monitor_enabled + channel_monitor_mode.
  952. func ProvideChannelMonitorService(
  953. repo ChannelMonitorRepository,
  954. encryptor SecretEncryptor,
  955. settingService *SettingService,
  956. ) *ChannelMonitorService {
  957. svc := NewChannelMonitorService(repo, encryptor)
  958. svc.SetRuntimeReader(settingService)
  959. return svc
  960. }
  961. // ProvideChannelMonitorRunner 创建并启动渠道监控调度器。
  962. // 通过 SetScheduler 注入回 service 后再 Start,确保启动时加载所有 enabled monitor,
  963. // 后续 CRUD 也能即时同步任务表。Runner.Stop 由 cleanup function 调用。
  964. // settingService 用于 runner 每次 fire 读取功能开关。
  965. // quotaFetcher(账号侧用量聚合)也在此注入:accountUsage/CN 服务在 wire 图中
  966. // 晚于 channelMonitorService 构造,走 setter 注入避免调整既有构造顺序。
  967. func ProvideChannelMonitorRunner(
  968. svc *ChannelMonitorService,
  969. settingService *SettingService,
  970. quotaFetcher *ChannelMonitorQuotaFetcher,
  971. ) *ChannelMonitorRunner {
  972. r := NewChannelMonitorRunner(svc, settingService)
  973. if svc != nil {
  974. // Ensure runtime reader is set even if ProvideChannelMonitorService
  975. // was constructed without settings (tests / alternate providers).
  976. svc.SetRuntimeReader(settingService)
  977. svc.SetScheduler(r)
  978. svc.SetQuotaFetcher(quotaFetcher)
  979. }
  980. r.Start()
  981. return r
  982. }
  983. // ProvideChannelMonitorV2Service wires settings for user-facing privacy flags
  984. // (e.g. hide RPM/TPM throughput).
  985. func ProvideChannelMonitorV2Service(repo ChannelMonitorV2Repository, settingService *SettingService) *ChannelMonitorV2Service {
  986. svc := NewChannelMonitorV2Service(repo)
  987. svc.SetRuntimeReader(settingService)
  988. return svc
  989. }
  990. // ProvideChannelMonitorV2Aggregator starts the passive minute-rollup worker.
  991. // Aggregation only runs when channel_monitor_enabled=true and mode=v2 (and V2 config enabled).
  992. // Set CHANNEL_MONITOR_V2_DISABLE_AGGREGATOR=1 to skip Start (local demo with seeded facts).
  993. func ProvideChannelMonitorV2Aggregator(repo ChannelMonitorV2Repository, db *sql.DB, settingService *SettingService) *ChannelMonitorV2Aggregator {
  994. aggregator := NewChannelMonitorV2Aggregator(repo, db, settingService)
  995. if os.Getenv("CHANNEL_MONITOR_V2_DISABLE_AGGREGATOR") == "1" {
  996. return aggregator
  997. }
  998. aggregator.Start()
  999. return aggregator
  1000. }