package main import ( "database/sql" "fmt" "time" _ "github.com/denisenkom/go-mssqldb" _ "github.com/go-sql-driver/mysql" "github.com/google/uuid" ) // DatabaseConfig 数据库配置结构 type DatabaseConfig struct { Enabled bool `json:"enabled"` Type string `json:"type"` // mysql 或 sqlserver Host string `json:"host"` Port int `json:"port"` Database string `json:"database"` Username string `json:"username"` Password string `json:"password"` SSLMode string `json:"ssl_mode,omitempty"` // for mysql } // Camera 摄像头数据库模型 type Camera struct { ID string `json:"id" db:"id"` Name string `json:"name" db:"name"` IP string `json:"ip" db:"ip"` Username string `json:"username" db:"username"` Password string `json:"password" db:"password"` RTSPURL string `json:"rtsp_url" db:"rtsp_url"` Enabled bool `json:"enabled" db:"enabled"` OnDemand bool `json:"on_demand" db:"on_demand"` Audio bool `json:"audio" db:"audio"` Debug bool `json:"debug" db:"debug"` CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt time.Time `json:"updated_at" db:"updated_at"` } // DatabaseManager 数据库管理器 type DatabaseManager struct { db *sql.DB config DatabaseConfig } // NewDatabaseManager 创建数据库管理器 func NewDatabaseManager(config DatabaseConfig) (*DatabaseManager, error) { if !config.Enabled { return nil, nil } var dsn string switch config.Type { case "mysql": dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", config.Username, config.Password, config.Host, config.Port, config.Database) if config.SSLMode != "" { dsn += "&tls=" + config.SSLMode } case "sqlserver": dsn = fmt.Sprintf("server=%s;port=%d;database=%s;user id=%s;password=%s", config.Host, config.Port, config.Database, config.Username, config.Password) default: return nil, fmt.Errorf("unsupported database type: %s", config.Type) } db, err := sql.Open(config.Type, dsn) if err != nil { return nil, fmt.Errorf("failed to open database: %v", err) } // 测试连接 if err := db.Ping(); err != nil { return nil, fmt.Errorf("failed to ping database: %v", err) } // 设置连接池参数 db.SetMaxOpenConns(25) db.SetMaxIdleConns(5) db.SetConnMaxLifetime(5 * time.Minute) dm := &DatabaseManager{ db: db, config: config, } // 初始化数据库表 if err := dm.initTables(); err != nil { return nil, fmt.Errorf("failed to initialize tables: %v", err) } return dm, nil } // initTables 初始化数据库表 func (dm *DatabaseManager) initTables() error { var createTableSQL string switch dm.config.Type { case "mysql": createTableSQL = ` CREATE TABLE IF NOT EXISTS cameras ( id VARCHAR(36) PRIMARY KEY, name VARCHAR(255) NOT NULL, ip VARCHAR(45) NOT NULL, username VARCHAR(100), password VARCHAR(100), rtsp_url TEXT NOT NULL, enabled BOOLEAN DEFAULT TRUE, on_demand BOOLEAN DEFAULT FALSE, audio BOOLEAN DEFAULT TRUE, debug BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_enabled (enabled), INDEX idx_ip (ip) )` case "sqlserver": createTableSQL = ` IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='cameras' AND xtype='U') CREATE TABLE cameras ( id NVARCHAR(36) PRIMARY KEY, name NVARCHAR(255) NOT NULL, ip NVARCHAR(45) NOT NULL, username NVARCHAR(100), password NVARCHAR(100), rtsp_url NTEXT NOT NULL, enabled BIT DEFAULT 1, on_demand BIT DEFAULT 0, audio BIT DEFAULT 1, debug BIT DEFAULT 0, created_at DATETIME2 DEFAULT GETDATE(), updated_at DATETIME2 DEFAULT GETDATE() )` } _, err := dm.db.Exec(createTableSQL) return err } // GetAllCameras 获取所有摄像头 func (dm *DatabaseManager) GetAllCameras() ([]Camera, error) { query := `SELECT id, name, ip, username, password, rtsp_url, enabled, on_demand, audio, debug, created_at, updated_at FROM cameras WHERE enabled = ?` rows, err := dm.db.Query(query, true) if err != nil { return nil, err } defer rows.Close() var cameras []Camera for rows.Next() { var camera Camera err := rows.Scan(&camera.ID, &camera.Name, &camera.IP, &camera.Username, &camera.Password, &camera.RTSPURL, &camera.Enabled, &camera.OnDemand, &camera.Audio, &camera.Debug, &camera.CreatedAt, &camera.UpdatedAt) if err != nil { return nil, err } cameras = append(cameras, camera) } return cameras, nil } // GetCameraByID 根据ID获取摄像头 func (dm *DatabaseManager) GetCameraByID(id string) (*Camera, error) { query := `SELECT id, name, ip, username, password, rtsp_url, enabled, on_demand, audio, debug, created_at, updated_at FROM cameras WHERE id = ?` var camera Camera err := dm.db.QueryRow(query, id).Scan(&camera.ID, &camera.Name, &camera.IP, &camera.Username, &camera.Password, &camera.RTSPURL, &camera.Enabled, &camera.OnDemand, &camera.Audio, &camera.Debug, &camera.CreatedAt, &camera.UpdatedAt) if err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, err } return &camera, nil } // CreateCamera 创建摄像头 func (dm *DatabaseManager) CreateCamera(camera *Camera) error { if camera.ID == "" { camera.ID = uuid.New().String() } camera.CreatedAt = time.Now() camera.UpdatedAt = time.Now() query := `INSERT INTO cameras (id, name, ip, username, password, rtsp_url, enabled, on_demand, audio, debug, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` _, err := dm.db.Exec(query, camera.ID, camera.Name, camera.IP, camera.Username, camera.Password, camera.RTSPURL, camera.Enabled, camera.OnDemand, camera.Audio, camera.Debug, camera.CreatedAt, camera.UpdatedAt) return err } // UpdateCamera 更新摄像头 func (dm *DatabaseManager) UpdateCamera(camera *Camera) error { camera.UpdatedAt = time.Now() query := `UPDATE cameras SET name=?, ip=?, username=?, password=?, rtsp_url=?, enabled=?, on_demand=?, audio=?, debug=?, updated_at=? WHERE id=?` _, err := dm.db.Exec(query, camera.Name, camera.IP, camera.Username, camera.Password, camera.RTSPURL, camera.Enabled, camera.OnDemand, camera.Audio, camera.Debug, camera.UpdatedAt, camera.ID) return err } // DeleteCamera 删除摄像头 func (dm *DatabaseManager) DeleteCamera(id string) error { query := `DELETE FROM cameras WHERE id = ?` _, err := dm.db.Exec(query, id) return err } // Close 关闭数据库连接 func (dm *DatabaseManager) Close() error { if dm.db != nil { return dm.db.Close() } return nil } // CameraToStream 将摄像头转换为流配置 func CameraToStream(camera Camera) StreamST { return StreamST{ Name: camera.Name, Channels: map[string]ChannelST{ "0": { Name: camera.Name, URL: camera.RTSPURL, OnDemand: camera.OnDemand, Debug: camera.Debug, Audio: camera.Audio, }, }, } } // StreamToCamera 将流配置转换为摄像头 func StreamToCamera(id string, stream StreamST) Camera { camera := Camera{ ID: id, Name: stream.Name, Enabled: true, CreatedAt: time.Now(), UpdatedAt: time.Now(), } // 获取第一个通道的配置 if len(stream.Channels) > 0 { for _, channel := range stream.Channels { camera.RTSPURL = channel.URL camera.OnDemand = channel.OnDemand camera.Debug = channel.Debug camera.Audio = channel.Audio break } } return camera }