package database import ( "fmt" "os" "path/filepath" "sync" "go.uber.org/zap" "gorm.io/driver/sqlite" "gorm.io/gorm" ) // Database 数据库接口,预留其他数据库接入 type Database interface { // GetDB 获取指定 bot 的数据库连接(使用表前缀区分不同 bot 的数据) GetDB(botID string) (*gorm.DB, error) // Close 关闭所有数据库连接 Close() error } // SQLiteDatabase SQLite 数据库实现 // 使用表前缀来区分不同 bot 的数据,所有 bot 共享同一个数据库文件和连接 type SQLiteDatabase struct { mu sync.RWMutex db *gorm.DB // 共享的数据库连接 dbs map[string]*gorm.DB // botID -> db (缓存,实际都指向同一个连接) logger *zap.Logger dbPath string options []gorm.Option } // NewSQLiteDatabase 创建 SQLite 数据库实例 // dbPath: 数据库文件路径(可以为空,使用默认路径) // 所有 bot 共享同一个数据库文件,通过表前缀区分 func NewSQLiteDatabase(logger *zap.Logger, dbPath string, options ...gorm.Option) Database { if dbPath == "" { dbPath = "data/cellbot.db" } return &SQLiteDatabase{ dbs: make(map[string]*gorm.DB), logger: logger.Named("database"), dbPath: dbPath, options: options, } } // GetDB 获取指定 bot 的数据库连接 // 使用表前缀来区分不同 bot 的数据,所有 bot 共享同一个数据库文件和连接 func (d *SQLiteDatabase) GetDB(botID string) (*gorm.DB, error) { if botID == "" { return nil, fmt.Errorf("botID cannot be empty") } // 初始化共享数据库连接(如果还没有) d.mu.Lock() if d.db == nil { // 确保数据库目录存在 dir := filepath.Dir(d.dbPath) if err := os.MkdirAll(dir, 0755); err != nil { d.mu.Unlock() return nil, fmt.Errorf("failed to create database directory: %w", err) } // 所有 bot 共享同一个数据库文件和连接 // 通过表前缀区分不同 bot 的数据 db, err := gorm.Open(sqlite.Open(d.dbPath), d.options...) if err != nil { d.mu.Unlock() return nil, fmt.Errorf("failed to open database: %w", err) } d.db = db d.logger.Info("Shared database connection created", zap.String("db_path", d.dbPath)) } d.mu.Unlock() // 所有 bot 返回同一个连接(通过缓存避免重复查找) d.mu.RLock() if cached, ok := d.dbs[botID]; ok { d.mu.RUnlock() return cached, nil } d.mu.RUnlock() // 缓存连接引用(实际都指向同一个连接) d.mu.Lock() if cached, ok := d.dbs[botID]; ok { d.mu.Unlock() return cached, nil } d.dbs[botID] = d.db d.mu.Unlock() return d.db, nil } // Close 关闭所有数据库连接 func (d *SQLiteDatabase) Close() error { d.mu.Lock() defer d.mu.Unlock() // 关闭共享的数据库连接 if d.db != nil { if sqlDB, err := d.db.DB(); err == nil { if err := sqlDB.Close(); err != nil { d.logger.Error("Failed to close database", zap.Error(err)) return err } } d.db = nil } // 清空缓存 d.dbs = make(map[string]*gorm.DB) d.logger.Info("Database connection closed") return nil } // GetDBPath 获取数据库文件路径 func (d *SQLiteDatabase) GetDBPath() string { return d.dbPath }