|
|
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
|
|
|
}
|