Helper 全局访问器

Helper 全局访问器

pkg/helper/ 提供了一组对常见基础设施服务的便捷访问函数。它们是对 container.MustGet[T]() 的薄薄一层封装,目标是让业务代码用起来像访问全局对象一样自然,但底层仍然是 DI 容器单例。

为什么有 Helper

虽然 DI 容器和反射注入功能强大,但在大多数业务文件中并不希望写:

db := container.MustGet[*gorm.DB]()

这种写法既冗长又需要导入 container 包。Helper 把最常用的基础设施抽出成顶层函数:

db := helper.GetDatabase()

行为完全等价(底层就是 container.MustGet),但更易读,且可在任意位置使用,无需先 Inject

函数列表

所有函数都位于 cnb.cool/mliev/open/go-web/pkg/helper

函数 返回类型 说明
GetDatabase() *gorm.DB GORM 数据库实例
GetLogger() gsr.Logger 全局结构化日志
GetCache() gsr.Cacher 缓存接口(根据 cache.driver 选择实现)
GetRedis() Redis 客户端 Redis 操作客户端
GetConfig() gsr.Provider 配置中心(Get* / Set / ... )
GetEnv() gsr.EnvReader Viper 环境变量读取器
GetRequestLogger(c) gsr.Logger 当前请求的 logger(带 traceId,从上下文取出)

注意:除 GetRequestLogger 外,所有函数都基于 MustGet,如果对应服务未注册或 Build() 失败,会 panic。这通常意味着你的 AppProvider.Assemblies() 没有把对应的 Assembly 放进装配链。

典型用法

数据库

import "cnb.cool/mliev/open/go-web/pkg/helper"

db := helper.GetDatabase()

var users []model.User
if err := db.Where("status = ?", "active").Find(&users).Error; err != nil {
    return err
}

日志(全局)

helper.GetLogger().Info("user created", gsr.String("username", u.Username))
helper.GetLogger().Error("update failed", gsr.Error(err))

日志(请求级,带 traceId)

在 Controller 或中间件里使用:

func (UserController) GetUser(c httpInterfaces.RouterContextInterface) {
    log := helper.GetRequestLogger(c)
    log.Info("handling get user", gsr.String("id", c.Param("id")))
    // ...
}

GetRequestLogger(c) 会从上下文中读取 helper.RequestLoggerKey 这个 key 对应的 logger;如果未设置,自动回退到 GetLogger()。框架默认在请求开始时由 HTTP server 注入一个带 traceId 字段的 logger,所以只要使用 GetRequestLogger 就能获得请求关联的日志

缓存

cache := helper.GetCache()

if val, err := cache.Get("user:42"); err == nil && val != nil {
    return val, nil
}

cache.Set("user:42", user, 5*time.Minute)

具体实现取决于 cache.driver 配置:memory / redis / none

Redis

ctx := c.Request.Context() // 或 context.Background()
client := helper.GetRedis()
if err := client.Set(ctx, "key", "value", 0).Err(); err != nil {
    return err
}

GetRedis() 返回的是经过封装的 Redis 客户端,具体类型见 pkg/server/redis/

配置

cfg := helper.GetConfig()
mode := cfg.GetString("app.mode", "debug")
port := cfg.GetInt("server.port", 8080)

环境变量

env := helper.GetEnv()
val := env.GetString("MY_VAR", "fallback")

通常优先使用 GetConfig,因为它是经过 InitConfig 默认值聚合的版本;只有在需要读取未在 autoload/ 中声明的配置项时才直接走 GetEnv

在测试中替换 Helper 返回值

由于 Helper 内部走的是 DI 容器,你可以在测试 setUp 阶段通过更高优先级的 Provider 注入 mock 实例:

func TestUserService(t *testing.T) {
    mockDB := newMockGormDB()
    container.Register(container.NewSimpleProviderWithPriority(
        reflect.TypeFor[*gorm.DB](),
        mockDB,
        100, // 高于默认优先级 0
    ))
    defer container.Reset(reflect.TypeFor[*gorm.DB]())

    // 此后 helper.GetDatabase() 返回 mockDB
    user, err := service.UserService{}.Create(...)
    // assertions ...
}

与直接 container.MustGet 的区别

维度 helper.GetDatabase() container.MustGet[*gorm.DB]()
易读性 ✅ 短 ❌ 冗长
显式依赖 ❌ 隐藏 ✅ 类型显眼
场景 业务代码 框架代码 / 测试

经验法则:业务层代码用 helper,框架层与单元测试用 container.MustGet[T]()