package database import ( "bufio" "database/sql" "fmt" "os" "path/filepath" "strings" "sync" "sunhpc/pkg/config" "sunhpc/pkg/logger" _ "github.com/go-sql-driver/mysql" _ "github.com/mattn/go-sqlite3" ) type DB struct { db *sql.DB logger logger.Logger } var ( dbInstance *DB dbOnce sync.Once dbErr error ) func GetInstance(dbConfig *config.DatabaseConfig, log logger.Logger) (*DB, error) { dbOnce.Do(func() { // 兜底: 未注入则使用全局默认日志实例 if log == nil { log = logger.DefaultLogger } log.Debugf("开始初始化数据库,路径: %s", dbConfig.Path) // 确认数据库目录存在 if err := os.MkdirAll(dbConfig.Path, 0755); err != nil { log.Errorf("创建数据库目录失败: %v", err) dbErr = err return } fullPath := filepath.Join(dbConfig.Path, dbConfig.Name) log.Debugf("数据库路径: %s", fullPath) // 构建DSN dsn := fmt.Sprintf("%s?_foreign_keys=on&_journal_mode=WAL&_timeout=5000&cache=shared", fullPath) log.Debugf("DSN: %s", dsn) // 打开SQLite 连接 sqlDB, err := sql.Open("sqlite3", dsn) if err != nil { log.Errorf("数据库打开失败: %v", err) dbErr = err return } // 设置连接池参数 sqlDB.SetMaxOpenConns(1) // SQLite 只支持单连接 sqlDB.SetMaxIdleConns(1) // 保持一个空闲连接 sqlDB.SetConnMaxLifetime(0) // 禁用连接生命周期超时 sqlDB.SetConnMaxIdleTime(0) // 禁用空闲连接超时 // 测试数据库连接 if err := sqlDB.Ping(); err != nil { sqlDB.Close() log.Errorf("数据库连接失败: %v", err) dbErr = err return } // 赋值 *DB 类型的单例(而非直接复制 *sql.DB) log.Info("数据库连接成功") dbInstance = &DB{sqlDB, log} }) if dbErr != nil { return nil, dbErr } return dbInstance, nil } // Close 关闭数据库连接 func (d *DB) Close() error { if d.db != nil { d.logger.Debug("关闭数据库连接") err := d.db.Close() if err != nil { d.logger.Errorf("数据库连接关闭失败: %v", err) return err } d.logger.Info("数据库连接关闭成功") return nil } return nil } func confirmAction(prompt string) bool { reader := bufio.NewReader(os.Stdin) logger.Warnf("%s [Y/Yes]: ", prompt) response, err := reader.ReadString('\n') if err != nil { return false } response = strings.ToLower(strings.TrimSpace(response)) return response == "y" || response == "yes" } func (d *DB) InitTables(force bool) error { d.logger.Info("开始初始化数据库表...") if force { // 确认是否强制删除 if !confirmAction("确认强制删除所有表和触发器?") { d.logger.Info("操作已取消") os.Exit(0) return nil } // 强制删除所有表和触发器 d.logger.Debug("强制删除所有表和触发器...") if err := dropTables(d.db); err != nil { return fmt.Errorf("删除表失败: %w", err) } d.logger.Debug("删除所有表和触发器成功") if err := dropTriggers(d.db); err != nil { return fmt.Errorf("删除触发器失败: %w", err) } d.logger.Debug("删除所有触发器成功") } // ✅ 调用 schema.go 中的函数 for _, ddl := range CreateTableStatements() { d.logger.Debugf("执行: %s", ddl) if _, err := d.db.Exec(ddl); err != nil { return fmt.Errorf("数据表创建失败: %w", err) } } d.logger.Info("数据库表创建成功") /* 使用sqlite3命令 测试数据库是否存在表 ✅ 查询所有表 sqlite3 /var/lib/sunhpc/sunhpc.db .tables # 查看所有表 select * from sqlite_master where type='table'; # 查看表定义 PRAGMA integrity_check; # 检查数据库完整性 */ return nil } func dropTables(db *sql.DB) error { // ✅ 调用 schema.go 中的函数 for _, table := range DropTableOrder() { if _, err := db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)); err != nil { return err } } return nil } func dropTriggers(db *sql.DB) error { // ✅ 调用 schema.go 中的函数 for _, trigger := range DropTriggerStatements() { if _, err := db.Exec(fmt.Sprintf("DROP TRIGGER IF EXISTS `%s`", trigger)); err != nil { return err } } return nil }