Files
OpenFlare/backend/plugins/domain/admin/service/service.go
T
ryan 4f50f6a8f9 feat(core): bind request services and implement registered tasks
Wire plugin services through Bind/InjectFrom and AppContext so HTTP and
workers resolve dependencies after Apply. Register TaskHandler objects
with persisted results, and implement send_email_code, mail:send,
cleanup_inactive_users, and dispatch_bot_msg.
2026-09-02 16:59:00 +08:00

227 lines
6.1 KiB
Go

// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
// Package service provides business logic and orchestration for the admin domain.
package service
import (
"Wavelet/core"
"Wavelet/core/contracts"
"Wavelet/plugins/domain/admin/errs"
"Wavelet/plugins/domain/admin/repository"
"context"
"errors"
"sync"
"gorm.io/gorm"
)
var (
servicesMu sync.RWMutex
dbService contracts.DBService
cacheService contracts.CacheService
userService contracts.UserService
authService contracts.AuthService
taskService contracts.TaskService
storageSvc contracts.StorageService
riskControlService contracts.RiskControlService
eventEmitter func(ctx context.Context, topic string, payload any) error
)
// SetDBService injects the DBService contract.
func SetDBService(s contracts.DBService) {
servicesMu.Lock()
defer servicesMu.Unlock()
dbService = s
repository.SetDBService(s)
}
// SetCacheService injects the CacheService contract.
func SetCacheService(s contracts.CacheService) {
servicesMu.Lock()
defer servicesMu.Unlock()
cacheService = s
repository.SetCacheService(s)
}
// SetUserService injects the UserService contract.
func SetUserService(s contracts.UserService) {
servicesMu.Lock()
defer servicesMu.Unlock()
userService = s
}
// SetAuthService injects the AuthService contract.
func SetAuthService(s contracts.AuthService) {
servicesMu.Lock()
defer servicesMu.Unlock()
authService = s
}
// SetTaskService injects the TaskService contract.
func SetTaskService(s contracts.TaskService) {
servicesMu.Lock()
defer servicesMu.Unlock()
taskService = s
}
// SetStorageService injects the StorageService contract.
func SetStorageService(s contracts.StorageService) {
servicesMu.Lock()
defer servicesMu.Unlock()
storageSvc = s
}
// SetRiskControlService injects the RiskControlService contract.
func SetRiskControlService(s contracts.RiskControlService) {
servicesMu.Lock()
defer servicesMu.Unlock()
riskControlService = s
}
// SetEventEmitter sets the event emission callback.
func SetEventEmitter(fn func(ctx context.Context, topic string, payload any) error) {
servicesMu.Lock()
defer servicesMu.Unlock()
eventEmitter = fn
}
// EmitEvent publishes a domain event if an emitter is registered.
func EmitEvent(ctx context.Context, topic string, payload any) error {
servicesMu.RLock()
defer servicesMu.RUnlock()
if eventEmitter == nil {
return nil
}
return eventEmitter(ctx, topic, payload)
}
// ResetServices clears all injected services (used on disposal and testing).
func ResetServices() {
servicesMu.Lock()
defer servicesMu.Unlock()
dbService = nil
cacheService = nil
userService = nil
authService = nil
taskService = nil
storageSvc = nil
riskControlService = nil
eventEmitter = nil
repository.ResetServices()
}
// GetDB returns the GORM DB instance bound to the context if available.
func GetDB(ctx context.Context) *gorm.DB {
if s, err := core.InjectFrom[contracts.DBService](ctx); err == nil && s != nil {
return s.DB(ctx)
}
servicesMu.RLock()
defer servicesMu.RUnlock()
if dbService == nil {
return nil
}
return dbService.DB(ctx)
}
// GetCache returns the unified CacheService instance.
func GetCache(ctx context.Context) contracts.CacheService {
if s, err := core.InjectFrom[contracts.CacheService](ctx); err == nil && s != nil {
return s
}
servicesMu.RLock()
defer servicesMu.RUnlock()
return cacheService
}
// GetUserService returns the UserService instance.
func GetUserService(ctx context.Context) contracts.UserService {
if s, err := core.InjectFrom[contracts.UserService](ctx); err == nil && s != nil {
return s
}
servicesMu.RLock()
defer servicesMu.RUnlock()
return userService
}
// GetAuthService returns the AuthService instance.
func GetAuthService(ctx context.Context) contracts.AuthService {
if s, err := core.InjectFrom[contracts.AuthService](ctx); err == nil && s != nil {
return s
}
servicesMu.RLock()
defer servicesMu.RUnlock()
return authService
}
// GetTaskService returns the TaskService instance.
func GetTaskService(ctx context.Context) contracts.TaskService {
if s, err := core.InjectFrom[contracts.TaskService](ctx); err == nil && s != nil {
return s
}
servicesMu.RLock()
defer servicesMu.RUnlock()
return taskService
}
// GetStorageService returns the StorageService instance.
func GetStorageService(ctx context.Context) contracts.StorageService {
if s, err := core.InjectFrom[contracts.StorageService](ctx); err == nil && s != nil {
return s
}
servicesMu.RLock()
defer servicesMu.RUnlock()
return storageSvc
}
// GetRiskControlService returns the RiskControlService instance.
func GetRiskControlService(ctx context.Context) contracts.RiskControlService {
if s, err := core.InjectFrom[contracts.RiskControlService](ctx); err == nil && s != nil {
return s
}
servicesMu.RLock()
defer servicesMu.RUnlock()
return riskControlService
}
// translateNotFound collapses the persistence layer's record-not-found sentinel into
// the plugin's own domain error so that no layer above the repository has to import gorm.
func translateNotFound(err error, notFound error) error {
if errors.Is(err, gorm.ErrRecordNotFound) {
return notFound
}
return err
}
// isRecordMissing reports whether err originates from a missing persistence row.
func isRecordMissing(err error) bool {
return errors.Is(err, gorm.ErrRecordNotFound)
}
// requireUserService resolves the injected user contract service.
func requireUserService(ctx context.Context) (contracts.UserService, error) {
userSvc := GetUserService(ctx)
if userSvc == nil {
return nil, errs.ErrUserServiceUnavailable
}
return userSvc, nil
}
// requireAuthService resolves the injected auth contract service.
func requireAuthService(ctx context.Context) (contracts.AuthService, error) {
authSvc := GetAuthService(ctx)
if authSvc == nil {
return nil, errs.ErrAuthServiceUnavailable
}
return authSvc, nil
}
// requireTaskService resolves the injected task contract service.
func requireTaskService(ctx context.Context) (contracts.TaskService, error) {
taskSvc := GetTaskService(ctx)
if taskSvc == nil {
return nil, errs.ErrTaskServiceUnavailable
}
return taskSvc, nil
}