如何在Go语言Discord Bot中无全局变量访问数据库连接?
问题描述
我正在使用Go语言开发一款Discord机器人,该机器人在收到用户有效输入时会对PostgreSQL数据库执行查询操作。
现有代码片段:
func checkNilErr(e error) { if e != nil { log.Panic(e) } } func InitDb() (*sql.DB, error) { db, err := dbRepo.SqlConfig(ConnectionStr) return db, err } func Run() { // 创建Discord会话 discord, err := discordgo.New("Bot " + BotToken) checkNilErr(err) postGres, postGresErr := InitDb() defer postGres.Close() // 函数终止后关闭PostgreSQL连接 checkNilErr(postGresErr) postGresRepo = dbRepo.NewCardRepo(postGres) // 添加事件处理器,机器人收到有效用户输入时执行数据库查询 discord.AddHandler(newMessage) // 向Discord服务器发送意图 discord.Identify.Intents = discordgo.IntentsAllWithoutPrivileged // 开启会话 err = discord.Open() checkNilErr(err) defer discord.Close() // 函数终止后关闭会话 // 保持机器人运行直到收到系统中断信号(Ctrl + C) fmt.Println("Bot running....") c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, os.Interrupt) <-c }
目前的核心困扰是discord.AddHandler(newMessage)函数:AddHandler仅接受固定签名的函数作为参数,比如:
func(s *discordgo.Session, m *discordgo.MessageCreate) func(s *discordgo.Session, m *discordgo.PresenceUpdate)
这导致无法直接将已初始化的数据库连接作为参数传入处理器函数。目前想到的两种方案都有明显缺陷:
- 使用全局变量,但希望尽可能避免这种方式
- 在AddHandler内部初始化数据库连接,会导致每次收到用户输入都重新建立/断开连接,性能极差
尝试过仓库接口模式但未解决问题,想知道是否存在无需全局变量就能在AddHandler内部访问已初始化数据库连接的方法。
解决方案
方法1:利用闭包捕获数据库连接
在Run函数内部定义符合AddHandler签名要求的闭包,让它直接捕获已初始化的数据库仓库实例,无需全局变量:
func Run() { // ... 省略已有的初始化代码 ... postGresRepo := dbRepo.NewCardRepo(postGres) // 定义闭包形式的消息处理器 messageHandler := func(s *discordgo.Session, m *discordgo.MessageCreate) { // 直接使用捕获的postGresRepo执行数据库操作 card, err := postGresRepo.GetCard(m.Content) if err != nil { s.ChannelMessageSend(m.ChannelID, "查询失败:"+err.Error()) return } s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("找到卡片:%v", card)) } // 将闭包传入AddHandler discord.AddHandler(messageHandler) // ... 省略后续会话启动代码 ... }
闭包会在Run函数执行时保留对postGresRepo的引用,后续每次触发消息事件都能复用已有的数据库连接。
方法2:用结构体封装处理器与依赖
如果机器人有多个事件处理器或需要管理更多依赖,可将依赖封装到结构体中,把事件处理逻辑定义为结构体方法:
// 定义包含依赖的机器人处理器结构体 type BotHandler struct { cardRepo dbRepo.CardRepo // 假设CardRepo是你的仓库接口类型 } // 实现符合AddHandler签名的消息处理方法 func (h *BotHandler) HandleNewMessage(s *discordgo.Session, m *discordgo.MessageCreate) { // 直接使用结构体中的cardRepo执行数据库操作 card, err := h.cardRepo.GetCard(m.Content) if err != nil { s.ChannelMessageSend(m.ChannelID, "查询失败:"+err.Error()) return } s.ChannelMessageSend(m.ChannelID, fmt.Sprintf("找到卡片:%v", card)) } func Run() { // ... 省略已有的初始化代码 ... postGresRepo := dbRepo.NewCardRepo(postGres) // 初始化处理器实例,注入依赖 botHandler := &BotHandler{ cardRepo: postGresRepo, } // 将结构体方法传入AddHandler(方法会自动适配签名) discord.AddHandler(botHandler.HandleNewMessage) // ... 省略后续会话启动代码 ... }
这种方式更适合复杂项目,依赖管理更清晰,也方便后续扩展其他功能或添加新的依赖项。
内容的提问来源于stack exchange,提问作者nicktsan
相关产品推荐
相关产品推荐

