package svc import ( "context" "fmt" "os" "time" "haixun-backend/internal/config" libmongo "haixun-backend/internal/library/mongo" libredis "haixun-backend/internal/library/redis" "haixun-backend/internal/library/validate" "haixun-backend/internal/middleware" aisettings "haixun-backend/internal/model/ai/usecase" authrepodomain "haixun-backend/internal/model/auth/domain/repository" authdomain "haixun-backend/internal/model/auth/domain/usecase" authrepo "haixun-backend/internal/model/auth/repository" authuc "haixun-backend/internal/model/auth/usecase" jobrepo "haixun-backend/internal/model/job/repository" jobusecase "haixun-backend/internal/model/job/usecase" memberdomain "haixun-backend/internal/model/member/domain/usecase" memberrepo "haixun-backend/internal/model/member/repository" memberuc "haixun-backend/internal/model/member/usecase" permissiondomain "haixun-backend/internal/model/permission/domain/usecase" permissionrepo "haixun-backend/internal/model/permission/repository" permissionuc "haixun-backend/internal/model/permission/usecase" settingrepo "haixun-backend/internal/model/setting/repository" settingusecase "haixun-backend/internal/model/setting/usecase" jobworker "haixun-backend/internal/worker/job" goredis "github.com/redis/go-redis/v9" "github.com/zeromicro/go-zero/rest" ) type ServiceContext struct { Config config.Config Validator *validate.Validate Mongo *libmongo.Client Redis *goredis.Client Setting settingusecase.UseCase AI aisettings.UseCase Job jobusecase.UseCase AuthToken authdomain.TokenUseCase Member memberdomain.UseCase Permission permissiondomain.UseCase // Middlewares mounted per route group via generate/api `middleware:` directive. AuthJWT rest.Middleware MemberAuth rest.Middleware stopWorker context.CancelFunc stopScheduler context.CancelFunc stopReaper context.CancelFunc } func NewServiceContext(c config.Config) *ServiceContext { ctx := context.Background() mongoClient, err := libmongo.NewClient(ctx, c.Mongo) if err != nil { panic(err) } redisClient := libredis.NewClient(c.Redis) settingRepository := settingrepo.NewMongoRepository(mongoClient.Database()) if err := settingRepository.EnsureIndexes(ctx); err != nil { panic(err) } settingUseCase := settingusecase.NewUseCase(settingRepository) var tokenRevokeStore authrepodomain.TokenRevokeStore if redisClient != nil { tokenRevokeStore = authrepo.NewRedisTokenRevokeStore(redisClient) } authTokenUseCase := authuc.NewTokenUseCase(c.Auth, tokenRevokeStore) memberRepository := memberrepo.NewMongoRepository(mongoClient.Database()) if err := memberRepository.EnsureIndexes(ctx); err != nil { panic(err) } memberUseCase := memberuc.NewUseCase(memberRepository, authTokenUseCase) permissionRepository := permissionrepo.NewMongoPermissionRepository(mongoClient.Database()) rolePermissionRepository := permissionrepo.NewMongoRolePermissionRepository(mongoClient.Database()) if err := permissionRepository.EnsureIndexes(ctx); err != nil { panic(err) } if err := rolePermissionRepository.EnsureIndexes(ctx); err != nil { panic(err) } permissionUseCase := permissionuc.NewUseCase(permissionRepository, rolePermissionRepository) if err := permissionUseCase.EnsureDefaultPermissions(ctx); err != nil { panic(err) } jobTemplateRepository := jobrepo.NewMongoTemplateRepository(mongoClient.Database()) jobRunRepository := jobrepo.NewMongoRunRepository(mongoClient.Database()) jobScheduleRepository := jobrepo.NewMongoScheduleRepository(mongoClient.Database()) jobEventRepository := jobrepo.NewMongoEventRepository(mongoClient.Database()) jobQueueRepository := jobrepo.NewRedisQueueRepository(redisClient) if err := jobTemplateRepository.EnsureIndexes(ctx); err != nil { panic(err) } if err := jobRunRepository.EnsureIndexes(ctx); err != nil { panic(err) } if err := jobScheduleRepository.EnsureIndexes(ctx); err != nil { panic(err) } if err := jobEventRepository.EnsureIndexes(ctx); err != nil { panic(err) } jobUseCase := jobusecase.NewUseCase(jobTemplateRepository, jobRunRepository, jobScheduleRepository, jobEventRepository, jobQueueRepository) if err := jobUseCase.EnsureDemoTemplate(ctx); err != nil { panic(err) } sc := &ServiceContext{ Config: c, Validator: validate.New(), Mongo: mongoClient, Redis: redisClient, Setting: settingUseCase, AI: aisettings.NewUseCase(), Job: jobUseCase, AuthToken: authTokenUseCase, Member: memberUseCase, Permission: permissionUseCase, } hostname, _ := os.Hostname() if c.JobWorker.Enabled && redisClient != nil { workerType := c.JobWorker.WorkerType if workerType == "" { workerType = "go" } workerID := c.JobWorker.WorkerID if workerID == "" { workerID = fmt.Sprintf("%s-%s-worker", hostname, workerType) } workerCtx, cancel := context.WithCancel(context.Background()) sc.stopWorker = cancel runner := jobworker.NewRunner(workerID, workerType, jobUseCase) go runner.Start(workerCtx) } if c.JobScheduler.Enabled && redisClient != nil { schedulerHolder := fmt.Sprintf("%s-scheduler", hostname) interval := time.Duration(c.JobScheduler.IntervalSeconds) * time.Second if interval <= 0 { interval = time.Minute } schedulerCtx, cancel := context.WithCancel(context.Background()) sc.stopScheduler = cancel scheduler := jobworker.NewScheduler(schedulerHolder, jobUseCase) go scheduler.Start(schedulerCtx, interval) } if c.JobReaper.Enabled && redisClient != nil { interval := time.Duration(c.JobReaper.IntervalSeconds) * time.Second if interval <= 0 { interval = 30 * time.Second } reaperCtx, cancel := context.WithCancel(context.Background()) sc.stopReaper = cancel reaper := jobworker.NewReaper(jobUseCase) go reaper.Start(reaperCtx, interval) } sc.AuthJWT = middleware.NewAuthJWTMiddleware(sc.AuthToken, sc.Config.Auth).Handle sc.MemberAuth = middleware.NewMemberAuthMiddleware(sc.AuthToken, sc.Config.Auth).Handle return sc } func (sc *ServiceContext) Close(ctx context.Context) { if sc == nil { return } if sc.stopWorker != nil { sc.stopWorker() } if sc.stopScheduler != nil { sc.stopScheduler() } if sc.stopReaper != nil { sc.stopReaper() } _ = sc.Mongo.Close(ctx) if sc.Redis != nil { _ = sc.Redis.Close() } }