You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
RTSPtoWeb/database.go

399 lines
14 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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
}