You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 12:57:24