package main import ( "database/sql" "fmt" "strconv" "time" _ "github.com/denisenkom/go-mssqldb" _ "github.com/go-sql-driver/mysql" ) // 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 摄像头数据库模型 - 适配qsc_camera表 type Camera struct { CameraID int `json:"camera_id" db:"camera_id"` IP string `json:"ip" db:"ip"` Port int `json:"port" db:"port"` Username sql.NullString `json:"username" db:"username"` Password sql.NullString `json:"password" db:"password"` URL string `json:"url" db:"url"` CameraProduce sql.NullString `json:"camera_produce" db:"camera_produce"` CameraName sql.NullString `json:"camera_name" db:"camera_name"` DeviceType sql.NullString `json:"device_type" db:"device_type"` UnitCode sql.NullString `json:"unit_code" db:"unit_code"` NvrProduce sql.NullString `json:"nvr_produce" db:"nvr_produce"` NvrPath sql.NullString `json:"nvr_path" db:"nvr_path"` PlayBack sql.NullString `json:"play_back" db:"play_back"` Enabled bool `json:"enabled" db:"enabled"` // 兼容字段,如果数据库中没有此列则默认为true DelFlag string `json:"del_flag" db:"del_flag"` CreateBy sql.NullString `json:"create_by" db:"create_by"` CreateTime sql.NullTime `json:"create_time" db:"create_time"` UpdateBy sql.NullString `json:"update_by" db:"update_by"` UpdateTime sql.NullTime `json:"update_time" db:"update_time"` UserID sql.NullInt64 `json:"user_id" db:"user_id"` DeptID sql.NullInt64 `json:"dept_id" db:"dept_id"` } // 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 获取所有摄像头列表 - 适配qsc_camera表 func (dm *DatabaseManager) GetAllCameras() ([]Camera, error) { // 首先尝试查询包含enabled列的SQL query := `SELECT camera_id, ip, port, username, password, url, camera_produce, camera_name, device_type, unit_code, nvr_produce, nvr_path, play_back, enabled, del_flag, create_by, create_time, update_by, update_time, user_id, dept_id FROM qsc_camera WHERE del_flag != '1'` rows, err := dm.db.Query(query) if err != nil { // 如果查询失败(可能是因为enabled列不存在),尝试不包含enabled列的查询 query = `SELECT camera_id, ip, port, username, password, url, camera_produce, camera_name, device_type, unit_code, nvr_produce, nvr_path, play_back, del_flag, create_by, create_time, update_by, update_time, user_id, dept_id FROM qsc_camera WHERE del_flag != '1'` rows, err = dm.db.Query(query) if err != nil { return nil, err } } defer rows.Close() var cameras []Camera for rows.Next() { var camera Camera // 检查列数来决定如何扫描 columns, _ := rows.Columns() if len(columns) == 21 { // 包含enabled列 err := rows.Scan(&camera.CameraID, &camera.IP, &camera.Port, &camera.Username, &camera.Password, &camera.URL, &camera.CameraProduce, &camera.CameraName, &camera.DeviceType, &camera.UnitCode, &camera.NvrProduce, &camera.NvrPath, &camera.PlayBack, &camera.Enabled, &camera.DelFlag, &camera.CreateBy, &camera.CreateTime, &camera.UpdateBy, &camera.UpdateTime, &camera.UserID, &camera.DeptID) if err != nil { return nil, err } } else { // 不包含enabled列 camera.Enabled = true // 默认启用 err := rows.Scan(&camera.CameraID, &camera.IP, &camera.Port, &camera.Username, &camera.Password, &camera.URL, &camera.CameraProduce, &camera.CameraName, &camera.DeviceType, &camera.UnitCode, &camera.NvrProduce, &camera.NvrPath, &camera.PlayBack, &camera.DelFlag, &camera.CreateBy, &camera.CreateTime, &camera.UpdateBy, &camera.UpdateTime, &camera.UserID, &camera.DeptID) if err != nil { return nil, err } } cameras = append(cameras, camera) } return cameras, nil } // GetCamerasByUnitCode 根据unitcode获取摄像头列表 - 适配qsc_camera表 func (dm *DatabaseManager) GetCamerasByUnitCode(unitCode string) ([]Camera, error) { // 首先尝试查询包含enabled列的SQL query := `SELECT camera_id, ip, port, username, password, url, camera_produce, camera_name, device_type, unit_code, nvr_produce, nvr_path, play_back, enabled, del_flag, create_by, create_time, update_by, update_time, user_id, dept_id FROM qsc_camera WHERE unit_code = ? AND del_flag != '1'` rows, err := dm.db.Query(query, unitCode) if err != nil { // 如果查询失败(可能是因为enabled列不存在),尝试不包含enabled列的查询 query = `SELECT camera_id, ip, port, username, password, url, camera_produce, camera_name, device_type, unit_code, nvr_produce, nvr_path, play_back, del_flag, create_by, create_time, update_by, update_time, user_id, dept_id FROM qsc_camera WHERE unit_code = ? AND del_flag != '1'` rows, err = dm.db.Query(query, unitCode) if err != nil { return nil, err } } defer rows.Close() var cameras []Camera for rows.Next() { var camera Camera // 检查列数来决定如何扫描 columns, _ := rows.Columns() if len(columns) == 21 { // 包含enabled列 err := rows.Scan(&camera.CameraID, &camera.IP, &camera.Port, &camera.Username, &camera.Password, &camera.URL, &camera.CameraProduce, &camera.CameraName, &camera.DeviceType, &camera.UnitCode, &camera.NvrProduce, &camera.NvrPath, &camera.PlayBack, &camera.Enabled, &camera.DelFlag, &camera.CreateBy, &camera.CreateTime, &camera.UpdateBy, &camera.UpdateTime, &camera.UserID, &camera.DeptID) if err != nil { return nil, err } } else { // 不包含enabled列 camera.Enabled = true // 默认启用 err := rows.Scan(&camera.CameraID, &camera.IP, &camera.Port, &camera.Username, &camera.Password, &camera.URL, &camera.CameraProduce, &camera.CameraName, &camera.DeviceType, &camera.UnitCode, &camera.NvrProduce, &camera.NvrPath, &camera.PlayBack, &camera.DelFlag, &camera.CreateBy, &camera.CreateTime, &camera.UpdateBy, &camera.UpdateTime, &camera.UserID, &camera.DeptID) if err != nil { return nil, err } } cameras = append(cameras, camera) } return cameras, nil } // GetCameraByID 根据ID获取摄像头 - 适配qsc_camera表 func (dm *DatabaseManager) GetCameraByID(id string) (*Camera, error) { query := `SELECT camera_id, ip, port, username, password, url, camera_produce, camera_name, device_type, unit_code, nvr_produce, nvr_path, play_back, del_flag, create_by, create_time, update_by, update_time, user_id, dept_id FROM qsc_camera WHERE camera_id = ? AND del_flag = '0'` var camera Camera err := dm.db.QueryRow(query, id).Scan(&camera.CameraID, &camera.IP, &camera.Port, &camera.Username, &camera.Password, &camera.URL, &camera.CameraProduce, &camera.CameraName, &camera.DeviceType, &camera.UnitCode, &camera.NvrProduce, &camera.NvrPath, &camera.PlayBack, &camera.DelFlag, &camera.CreateBy, &camera.CreateTime, &camera.UpdateBy, &camera.UpdateTime, &camera.UserID, &camera.DeptID) if err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, err } return &camera, nil } // CreateCamera 创建摄像头 - 适配qsc_camera表 func (dm *DatabaseManager) CreateCamera(camera *Camera) error { camera.CreateTime = sql.NullTime{Time: time.Now(), Valid: true} camera.UpdateTime = sql.NullTime{Time: time.Now(), Valid: true} query := `INSERT INTO qsc_camera (ip, port, username, password, url, camera_produce, camera_name, device_type, unit_code, nvr_produce, nvr_path, play_back, del_flag, create_by, create_time, update_by, update_time, user_id, dept_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` _, err := dm.db.Exec(query, camera.IP, camera.Port, camera.Username, camera.Password, camera.URL, camera.CameraProduce, camera.CameraName, camera.DeviceType, camera.UnitCode, camera.NvrProduce, camera.NvrPath, camera.PlayBack, camera.DelFlag, camera.CreateBy, camera.CreateTime, camera.UpdateBy, camera.UpdateTime, camera.UserID, camera.DeptID) return err } // UpdateCamera 更新摄像头 - 适配qsc_camera表 func (dm *DatabaseManager) UpdateCamera(camera *Camera) error { camera.UpdateTime = sql.NullTime{Time: time.Now(), Valid: true} query := `UPDATE qsc_camera SET ip=?, port=?, username=?, password=?, url=?, camera_produce=?, camera_name=?, device_type=?, unit_code=?, nvr_produce=?, nvr_path=?, play_back=?, update_by=?, update_time=? WHERE camera_id=?` _, err := dm.db.Exec(query, camera.IP, camera.Port, camera.Username, camera.Password, camera.URL, camera.CameraProduce, camera.CameraName, camera.DeviceType, camera.UnitCode, camera.NvrProduce, camera.NvrPath, camera.PlayBack, camera.UpdateBy, camera.UpdateTime, camera.CameraID) return err } // DeleteCamera 删除摄像头 - 适配qsc_camera表 func (dm *DatabaseManager) DeleteCamera(id string) error { query := `UPDATE qsc_camera SET del_flag = '1' WHERE camera_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 将摄像头转换为流配置 - 适配qsc_camera表 func CameraToStream(camera Camera) StreamST { // 构建RTSP URL rtspURL := camera.URL if rtspURL == "" { // 如果URL为空,根据IP、端口、用户名密码构建RTSP URL if camera.Username.Valid && camera.Password.Valid && camera.Username.String != "" && camera.Password.String != "" { rtspURL = fmt.Sprintf("rtsp://%s:%s@%s:%d/stream1", camera.Username.String, camera.Password.String, camera.IP, camera.Port) } else { rtspURL = fmt.Sprintf("rtsp://%s:%d/stream1", camera.IP, camera.Port) } } cameraName := "Unknown Camera" if camera.CameraName.Valid { cameraName = camera.CameraName.String } return StreamST{ Name: cameraName, Channels: map[string]ChannelST{ "0": { Name: cameraName, URL: rtspURL, OnDemand: false, // 根据需要设置 Debug: false, // 根据需要设置 Audio: true, // 根据需要设置 }, }, } } // StreamToCamera 将流配置转换为摄像头 - 适配qsc_camera表 func StreamToCamera(id string, stream StreamST) Camera { // 创建基础摄像头对象 - 适配qsc_camera表 cameraID, _ := strconv.Atoi(id) camera := Camera{ CameraID: cameraID, CameraName: sql.NullString{String: stream.Name, Valid: true}, IP: "", // 从URL中解析 Port: 554, // 默认RTSP端口 Username: sql.NullString{String: "", Valid: false}, // 从URL中解析 Password: sql.NullString{String: "", Valid: false}, // 从URL中解析 URL: "", // 将从第一个通道获取 DeviceType: sql.NullString{String: "网络摄像头", Valid: true}, UnitCode: sql.NullString{String: "DEFAULT", Valid: true}, DelFlag: "0", CreateBy: sql.NullString{String: "system", Valid: true}, CreateTime: sql.NullTime{Time: time.Now(), Valid: true}, UpdateBy: sql.NullString{String: "system", Valid: true}, UpdateTime: sql.NullTime{Time: time.Now(), Valid: true}, UserID: sql.NullInt64{Int64: 1, Valid: true}, DeptID: sql.NullInt64{Int64: 1, Valid: true}, } // 获取第一个通道的配置 if len(stream.Channels) > 0 { for _, channel := range stream.Channels { camera.URL = channel.URL break } } return camera }