Files
backend/internal/app/infra_module.go

109 lines
3.1 KiB
Go
Raw Normal View History

package app
import (
"context"
"carrotskin/pkg/auth"
"carrotskin/pkg/config"
"carrotskin/pkg/database"
"carrotskin/pkg/email"
"carrotskin/pkg/logger"
"carrotskin/pkg/redis"
"carrotskin/pkg/storage"
"go.uber.org/fx"
"go.uber.org/zap"
"gorm.io/gorm"
)
// InfraModule 注册所有基础设施依赖Logger/DB/Redis/Storage/Email/JWT/Casbin。
// 每个需要资源释放的组件都通过 fx.Lifecycle 注册 OnStop 钩子,
// 彻底替代 main.go 里分散的 defer xxx.Close()。
var InfraModule = fx.Options(
// Logger
fx.Provide(logger.New),
// Database: 同时暴露 *database.DB封装带连接池统计和 *gorm.DB裸 GORM
fx.Provide(provideDatabase),
// 暴露 *gorm.DB 供 Repository/Casbin 等消费
fx.Provide(func(d *database.DB) *gorm.DB { return d.DB }),
// Redis含 miniredis 测试回退)
fx.Provide(provideRedis),
// 对象存储S3 兼容)
fx.Provide(provideStorage),
// 邮件服务
fx.Provide(email.NewService),
// JWT 服务(应用层认证)
fx.Provide(func(cfg *config.Config) *auth.JWTService {
return auth.NewJWTService(cfg.JWT.Secret, cfg.JWT.ExpireHours)
}),
// Casbin 权限服务
fx.Provide(func(db *gorm.DB, cfg *config.Config, log *zap.Logger) (*auth.CasbinService, error) {
return auth.NewCasbinService(db, cfg.Casbin.ModelPath, log)
}),
)
// provideDatabase 创建数据库连接并注册关闭钩子。
func provideDatabase(cfg *config.Config, lc fx.Lifecycle, log *zap.Logger) (*database.DB, error) {
db, err := database.New(cfg.Database)
if err != nil {
return nil, err
}
lc.Append(fx.Hook{
OnStop: func(ctx context.Context) error {
log.Info("正在关闭数据库连接")
return db.Close()
},
})
return db, nil
}
// provideRedis 创建 Redis 客户端并注册关闭钩子。
// 开发/测试环境下若连接失败会自动回退到 miniredis。
func provideRedis(cfg *config.Config, lc fx.Lifecycle, log *zap.Logger) (*redis.Client, error) {
client, err := redis.New(cfg.Redis, log)
if err != nil {
// 检查是否允许回退到 miniredis仅开发/测试环境)
if redis.AllowFallbackToMiniRedis() {
log.Warn("Redis连接失败尝试使用miniredis回退", zap.Error(err))
client, err = redis.InitMiniRedis(log)
if err != nil {
return nil, err
}
log.Info("已回退到miniredis用于开发/测试环境")
} else {
return nil, err
}
}
lc.Append(fx.Hook{
OnStop: func(ctx context.Context) error {
log.Info("正在关闭 Redis 连接")
return client.Close()
},
})
return client, nil
}
// provideStorage 创建对象存储客户端并注册关闭钩子。
// 存储服务在配置缺失时可能是可选的,失败时返回 error 让调用方决定是否降级。
func provideStorage(cfg *config.Config, lc fx.Lifecycle, log *zap.Logger) (*storage.StorageClient, error) {
client, err := storage.NewStorage(cfg.RustFS)
if err != nil {
return nil, err
}
// MinIO 客户端无需显式 Close但保留钩子以备未来扩展
lc.Append(fx.Hook{
OnStop: func(ctx context.Context) error {
log.Info("对象存储客户端已停止")
return nil
},
})
return client, nil
}