Files
cellbot/internal/database/database.go
lafay fb5fae1524 chore: update project structure and enhance plugin functionality
- Added new entries to .gitignore for database files.
- Updated go.mod and go.sum to include new indirect dependencies for database and ORM support.
- Refactored event handling to improve message reply functionality in the protocol.
- Enhanced the dispatcher to allow for better event processing and logging.
- Removed outdated plugin documentation and unnecessary files to streamline the codebase.
- Improved welcome message formatting and screenshot options for better user experience.
2026-01-05 05:14:31 +08:00

126 lines
3.1 KiB
Go

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
}