feat(数据库): 增强数据库集成功能并改进空值处理

- 在storageConfig.go中添加数据库启用状态同步逻辑
- 修改Camera结构体字段为sql.Null类型以更好处理数据库空值
- 新增DatabaseGetCameras和DatabaseGetCamera方法用于摄像头查询
- 在apiHTTPCamera.go中添加空值处理辅助函数
- 重构Java集成API以使用新的数据库结构和空值处理
- 更新config.json中的数据库凭证
ziyun-rtsp-web
karlkyo 8 months ago
parent 6a473d0732
commit 1a617370d6

@ -1,6 +1,7 @@
package main
import (
"database/sql"
"fmt"
"net/http"
"time"
@ -9,6 +10,30 @@ import (
"github.com/sirupsen/logrus"
)
// getStringValue 获取sql.NullString的值
func getStringValue(ns sql.NullString) string {
if ns.Valid {
return ns.String
}
return ""
}
// getInt64Value 获取sql.NullInt64的值
func getInt64Value(ni sql.NullInt64) int64 {
if ni.Valid {
return ni.Int64
}
return 0
}
// getTimeValue 获取sql.NullTime的值
func getTimeValue(nt sql.NullTime) time.Time {
if nt.Valid {
return nt.Time
}
return time.Time{}
}
// CameraRequest 摄像头请求结构
type CameraRequest struct {
Name string `json:"name" binding:"required"`
@ -82,23 +107,23 @@ func HTTPAPIServerCameras(c *gin.Context) {
CameraID: camera.CameraID,
IP: camera.IP,
Port: camera.Port,
Username: camera.Username,
Password: camera.Password,
Username: getStringValue(camera.Username),
Password: getStringValue(camera.Password),
URL: camera.URL,
CameraProduce: camera.CameraProduce,
CameraName: camera.CameraName,
DeviceType: camera.DeviceType,
UnitCode: camera.UnitCode,
NvrProduce: camera.NvrProduce,
NvrPath: camera.NvrPath,
PlayBack: camera.PlayBack,
CameraProduce: getStringValue(camera.CameraProduce),
CameraName: getStringValue(camera.CameraName),
DeviceType: getStringValue(camera.DeviceType),
UnitCode: getStringValue(camera.UnitCode),
NvrProduce: getStringValue(camera.NvrProduce),
NvrPath: getStringValue(camera.NvrPath),
PlayBack: getStringValue(camera.PlayBack),
DelFlag: camera.DelFlag,
CreateBy: camera.CreateBy,
CreateTime: camera.CreateTime,
UpdateBy: camera.UpdateBy,
UpdateTime: camera.UpdateTime,
UserID: camera.UserID,
DeptID: camera.DeptID,
CreateBy: getStringValue(camera.CreateBy),
CreateTime: getTimeValue(camera.CreateTime),
UpdateBy: getStringValue(camera.UpdateBy),
UpdateTime: getTimeValue(camera.UpdateTime),
UserID: getInt64Value(camera.UserID),
DeptID: getInt64Value(camera.DeptID),
StreamStatus: streamStatus,
}
responses = append(responses, response)
@ -156,23 +181,23 @@ func HTTPAPIServerCamerasByUnitCode(c *gin.Context) {
CameraID: camera.CameraID,
IP: camera.IP,
Port: camera.Port,
Username: camera.Username,
Password: camera.Password,
Username: getStringValue(camera.Username),
Password: getStringValue(camera.Password),
URL: camera.URL,
CameraProduce: camera.CameraProduce,
CameraName: camera.CameraName,
DeviceType: camera.DeviceType,
UnitCode: camera.UnitCode,
NvrProduce: camera.NvrProduce,
NvrPath: camera.NvrPath,
PlayBack: camera.PlayBack,
CameraProduce: getStringValue(camera.CameraProduce),
CameraName: getStringValue(camera.CameraName),
DeviceType: getStringValue(camera.DeviceType),
UnitCode: getStringValue(camera.UnitCode),
NvrProduce: getStringValue(camera.NvrProduce),
NvrPath: getStringValue(camera.NvrPath),
PlayBack: getStringValue(camera.PlayBack),
DelFlag: camera.DelFlag,
CreateBy: camera.CreateBy,
CreateTime: camera.CreateTime,
UpdateBy: camera.UpdateBy,
UpdateTime: camera.UpdateTime,
UserID: camera.UserID,
DeptID: camera.DeptID,
CreateBy: getStringValue(camera.CreateBy),
CreateTime: getTimeValue(camera.CreateTime),
UpdateBy: getStringValue(camera.UpdateBy),
UpdateTime: getTimeValue(camera.UpdateTime),
UserID: getInt64Value(camera.UserID),
DeptID: getInt64Value(camera.DeptID),
StreamStatus: streamStatus,
}
responses = append(responses, response)
@ -206,19 +231,19 @@ func HTTPAPIServerCameraAdd(c *gin.Context) {
camera := Camera{
IP: req.IP,
Port: 554, // 默认RTSP端口
Username: req.Username,
Password: req.Password,
Username: sql.NullString{String: req.Username, Valid: req.Username != ""},
Password: sql.NullString{String: req.Password, Valid: req.Password != ""},
URL: req.RTSPURL,
CameraName: req.Name,
DeviceType: "网络摄像头",
UnitCode: "DEFAULT", // 默认单位代码
CameraName: sql.NullString{String: req.Name, Valid: true},
DeviceType: sql.NullString{String: "网络摄像头", Valid: true},
UnitCode: sql.NullString{String: "DEFAULT", Valid: true}, // 默认单位代码
DelFlag: "0",
CreateBy: "admin",
CreateTime: time.Now(),
UpdateBy: "admin",
UpdateTime: time.Now(),
UserID: 1,
DeptID: 1,
CreateBy: sql.NullString{String: "admin", Valid: true},
CreateTime: sql.NullTime{Time: time.Now(), Valid: true},
UpdateBy: sql.NullString{String: "admin", Valid: true},
UpdateTime: sql.NullTime{Time: time.Now(), Valid: true},
UserID: sql.NullInt64{Int64: 1, Valid: true},
DeptID: sql.NullInt64{Int64: 1, Valid: true},
}
// 保存到数据库
@ -314,12 +339,12 @@ func HTTPAPIServerCameraUpdate(c *gin.Context) {
// 更新摄像头信息 - 适配qsc_camera表
updatedCamera := *existingCamera // 复制现有摄像头信息
updatedCamera.IP = req.IP
updatedCamera.Username = req.Username
updatedCamera.Password = req.Password
updatedCamera.Username = sql.NullString{String: req.Username, Valid: req.Username != ""}
updatedCamera.Password = sql.NullString{String: req.Password, Valid: req.Password != ""}
updatedCamera.URL = req.RTSPURL
updatedCamera.CameraName = req.Name
updatedCamera.UpdateBy = "admin"
updatedCamera.UpdateTime = time.Now()
updatedCamera.CameraName = sql.NullString{String: req.Name, Valid: true}
updatedCamera.UpdateBy = sql.NullString{String: "admin", Valid: true}
updatedCamera.UpdateTime = sql.NullTime{Time: time.Now(), Valid: true}
// 更新数据库
err = Storage.dbManager.UpdateCamera(&updatedCamera)
@ -465,19 +490,19 @@ func HTTPAPIServerCameraGet(c *gin.Context) {
response := CameraResponse{
CameraID: camera.CameraID,
CameraName: camera.CameraName,
CameraName: getStringValue(camera.CameraName),
IP: camera.IP,
Port: camera.Port,
Username: camera.Username,
Username: getStringValue(camera.Username),
// Password: camera.Password, // 不返回密码
URL: camera.URL,
CameraProduce: camera.CameraProduce,
DeviceType: camera.DeviceType,
UnitCode: camera.UnitCode,
NvrProduce: camera.NvrProduce,
CameraProduce: getStringValue(camera.CameraProduce),
DeviceType: getStringValue(camera.DeviceType),
UnitCode: getStringValue(camera.UnitCode),
NvrProduce: getStringValue(camera.NvrProduce),
StreamStatus: status,
CreateTime: camera.CreateTime,
UpdateTime: camera.UpdateTime,
CreateTime: getTimeValue(camera.CreateTime),
UpdateTime: getTimeValue(camera.UpdateTime),
}
c.JSON(http.StatusOK, response)

@ -1,7 +1,6 @@
package main
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
@ -30,22 +29,22 @@ type StreamListResponse struct {
Streams []Stream `json:"streams"`
}
// Camera 摄像头信息结构
type Camera struct {
ID string `json:"id"`
Name string `json:"name"`
IP string `json:"ip"`
Port int `json:"port"`
Username string `json:"username"`
Password string `json:"password,omitempty"` // 敏感信息可选返回
RTSPURL string `json:"rtsp_url"`
Status string `json:"status"`
Enabled bool `json:"enabled"`
DeviceType string `json:"device_type"`
UnitCode string `json:"unit_code"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
PlayURLs PlayURLs `json:"play_urls"`
// JavaCamera 摄像头信息结构 - 用于Java集成API
type JavaCamera struct {
ID string `json:"id"`
Name string `json:"name"`
IP string `json:"ip"`
Port int `json:"port"`
Username string `json:"username"`
Password string `json:"password,omitempty"` // 敏感信息可选返回
RTSPURL string `json:"rtsp_url"`
Status string `json:"status"`
Enabled bool `json:"enabled"`
DeviceType string `json:"device_type"`
UnitCode string `json:"unit_code"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
PlayURLs PlayURLs `json:"play_urls"`
}
// Stream 流信息结构
@ -81,11 +80,11 @@ func HTTPJavaAPICameras(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
size, _ := strconv.Atoi(c.DefaultQuery("size", "20"))
var cameras []Camera
var cameras []JavaCamera
var total int
// 如果数据库启用,从数据库获取
if Storage.ServerDatabaseEnabled() {
if Storage.Server.DatabaseEnabled {
dbCameras, err := Storage.DatabaseGetCameras()
if err != nil {
c.JSON(http.StatusInternalServerError, JavaAPIResponse{
@ -98,20 +97,20 @@ func HTTPJavaAPICameras(c *gin.Context) {
// 转换数据库摄像头为API格式
for _, dbCamera := range dbCameras {
camera := Camera{
ID: dbCamera.ID,
Name: dbCamera.Name,
camera := JavaCamera{
ID: fmt.Sprintf("%d", dbCamera.CameraID),
Name: getStringValue(dbCamera.CameraName),
IP: dbCamera.IP,
Port: dbCamera.Port,
Username: dbCamera.Username,
RTSPURL: dbCamera.RTSPURL,
Status: dbCamera.Status,
Enabled: dbCamera.Enabled,
DeviceType: dbCamera.DeviceType,
UnitCode: dbCamera.UnitCode,
CreatedAt: dbCamera.CreatedAt,
UpdatedAt: dbCamera.UpdatedAt,
PlayURLs: generatePlayURLs(dbCamera.ID),
Username: getStringValue(dbCamera.Username),
RTSPURL: dbCamera.URL,
Status: "online", // 默认状态
Enabled: dbCamera.DelFlag == "0", // 删除标志为0表示启用
DeviceType: getStringValue(dbCamera.DeviceType),
UnitCode: getStringValue(dbCamera.UnitCode),
CreatedAt: getTimeValue(dbCamera.CreateTime),
UpdatedAt: getTimeValue(dbCamera.UpdateTime),
PlayURLs: generatePlayURLs(fmt.Sprintf("%d", dbCamera.CameraID)),
}
// 过滤条件
@ -126,9 +125,9 @@ func HTTPJavaAPICameras(c *gin.Context) {
}
} else {
// 从流配置获取
streamsList := Storage.StreamsList()
streamsList := Storage.Streams
for streamID := range streamsList {
camera := Camera{
camera := JavaCamera{
ID: streamID,
Name: fmt.Sprintf("Stream %s", streamID),
Status: "online",
@ -145,7 +144,7 @@ func HTTPJavaAPICameras(c *gin.Context) {
start := (page - 1) * size
end := start + size
if start > total {
cameras = []Camera{}
cameras = []JavaCamera{}
} else if end > total {
cameras = cameras[start:]
} else {
@ -156,9 +155,9 @@ func HTTPJavaAPICameras(c *gin.Context) {
Code: 200,
Message: "Success",
Success: true,
Data: CameraListResponse{
Total: total,
Cameras: cameras,
Data: map[string]interface{}{
"total": total,
"cameras": cameras,
},
}
@ -179,7 +178,7 @@ func HTTPJavaAPIStreams(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
size, _ := strconv.Atoi(c.DefaultQuery("size", "20"))
streamsList := Storage.StreamsList()
streamsList := Storage.Streams
var streams []Stream
for streamID, streamData := range streamsList {
@ -191,7 +190,7 @@ func HTTPJavaAPIStreams(c *gin.Context) {
}
// 获取通道信息
if streamData != nil {
if len(streamData.Channels) > 0 {
for channelID := range streamData.Channels {
stream.Channels[channelID] = fmt.Sprintf("Channel %s", channelID)
}
@ -238,7 +237,7 @@ func HTTPJavaAPICameraDetail(c *gin.Context) {
return
}
if Storage.ServerDatabaseEnabled() {
if Storage.Server.DatabaseEnabled {
camera, err := Storage.DatabaseGetCamera(cameraID)
if err != nil {
c.JSON(http.StatusNotFound, JavaAPIResponse{
@ -249,20 +248,20 @@ func HTTPJavaAPICameraDetail(c *gin.Context) {
return
}
apiCamera := Camera{
ID: camera.ID,
Name: camera.Name,
apiCamera := JavaCamera{
ID: fmt.Sprintf("%d", camera.CameraID),
Name: getStringValue(camera.CameraName),
IP: camera.IP,
Port: camera.Port,
Username: camera.Username,
RTSPURL: camera.RTSPURL,
Status: camera.Status,
Enabled: camera.Enabled,
DeviceType: camera.DeviceType,
UnitCode: camera.UnitCode,
CreatedAt: camera.CreatedAt,
UpdatedAt: camera.UpdatedAt,
PlayURLs: generatePlayURLs(camera.ID),
Username: getStringValue(camera.Username),
RTSPURL: camera.URL,
Status: "online",
Enabled: camera.DelFlag == "0",
DeviceType: getStringValue(camera.DeviceType),
UnitCode: getStringValue(camera.UnitCode),
CreatedAt: getTimeValue(camera.CreateTime),
UpdatedAt: getTimeValue(camera.UpdateTime),
PlayURLs: generatePlayURLs(fmt.Sprintf("%d", camera.CameraID)),
}
c.JSON(http.StatusOK, JavaAPIResponse{
@ -282,7 +281,7 @@ func HTTPJavaAPICameraDetail(c *gin.Context) {
return
}
camera := Camera{
camera := JavaCamera{
ID: cameraID,
Name: fmt.Sprintf("Stream %s", cameraID),
Status: "online",
@ -303,19 +302,19 @@ func HTTPJavaAPICameraDetail(c *gin.Context) {
func HTTPJavaAPISystemInfo(c *gin.Context) {
systemInfo := map[string]interface{}{
"version": "1.0.0",
"database_enabled": Storage.ServerDatabaseEnabled(),
"database_enabled": Storage.Server.DatabaseEnabled,
"demo_enabled": Storage.ServerHTTPDemo(),
"total_streams": len(Storage.StreamsList()),
"total_streams": len(Storage.Streams),
"server_time": time.Now().Format(time.RFC3339),
}
if Storage.ServerDatabaseEnabled() {
if Storage.Server.DatabaseEnabled {
cameras, err := Storage.DatabaseGetCameras()
if err == nil {
systemInfo["total_cameras"] = len(cameras)
onlineCount := 0
for _, camera := range cameras {
if camera.Status == "online" {
if camera.DelFlag == "0" { // 删除标志为0表示在线
onlineCount++
}
}
@ -340,4 +339,4 @@ func generatePlayURLs(streamID string) PlayURLs {
MSE: fmt.Sprintf("%s/pages/player/mse/%s/0", baseURL, streamID),
All: fmt.Sprintf("%s/pages/player/all/%s/0", baseURL, streamID),
}
}
}

@ -29,8 +29,8 @@
"type": "mysql",
"host": "47.96.78.103",
"port": 6330,
"username": "root",
"password": "Jgzy@#%6330^awSl",
"username": "JgzY",
"password": "JgzY@^&-)XswL",
"database": "ziyun",
"table_name": "qsc_camera"
}

@ -24,26 +24,26 @@ type DatabaseConfig struct {
// 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 string `json:"username" db:"username"`
Password string `json:"password" db:"password"`
URL string `json:"url" db:"url"`
CameraProduce string `json:"camera_produce" db:"camera_produce"`
CameraName string `json:"camera_name" db:"camera_name"`
DeviceType string `json:"device_type" db:"device_type"`
UnitCode string `json:"unit_code" db:"unit_code"`
NvrProduce string `json:"nvr_produce" db:"nvr_produce"`
NvrPath string `json:"nvr_path" db:"nvr_path"`
PlayBack string `json:"play_back" db:"play_back"`
DelFlag string `json:"del_flag" db:"del_flag"`
CreateBy string `json:"create_by" db:"create_by"`
CreateTime time.Time `json:"create_time" db:"create_time"`
UpdateBy string `json:"update_by" db:"update_by"`
UpdateTime time.Time `json:"update_time" db:"update_time"`
UserID int64 `json:"user_id" db:"user_id"`
DeptID int64 `json:"dept_id" db:"dept_id"`
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"`
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 数据库管理器
@ -233,8 +233,8 @@ func (dm *DatabaseManager) GetCameraByID(id string) (*Camera, error) {
// CreateCamera 创建摄像头 - 适配qsc_camera表
func (dm *DatabaseManager) CreateCamera(camera *Camera) error {
camera.CreateTime = time.Now()
camera.UpdateTime = time.Now()
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,
@ -252,7 +252,7 @@ func (dm *DatabaseManager) CreateCamera(camera *Camera) error {
// UpdateCamera 更新摄像头 - 适配qsc_camera表
func (dm *DatabaseManager) UpdateCamera(camera *Camera) error {
camera.UpdateTime = time.Now()
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=?,
@ -287,19 +287,24 @@ func CameraToStream(camera Camera) StreamST {
rtspURL := camera.URL
if rtspURL == "" {
// 如果URL为空根据IP、端口、用户名密码构建RTSP URL
if camera.Username != "" && camera.Password != "" {
if camera.Username.Valid && camera.Password.Valid && camera.Username.String != "" && camera.Password.String != "" {
rtspURL = fmt.Sprintf("rtsp://%s:%s@%s:%d/stream1",
camera.Username, camera.Password, camera.IP, camera.Port)
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: camera.CameraName,
Name: cameraName,
Channels: map[string]ChannelST{
"0": {
Name: camera.CameraName,
Name: cameraName,
URL: rtspURL,
OnDemand: false, // 根据需要设置
Debug: false, // 根据需要设置
@ -315,21 +320,21 @@ func StreamToCamera(id string, stream StreamST) Camera {
cameraID, _ := strconv.Atoi(id)
camera := Camera{
CameraID: cameraID,
CameraName: stream.Name,
IP: "", // 从URL中解析
Port: 554, // 默认RTSP端口
Username: "", // 从URL中解析
Password: "", // 从URL中解析
URL: "", // 将从第一个通道获取
DeviceType: "网络摄像头",
UnitCode: "DEFAULT",
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: "system",
CreateTime: time.Now(),
UpdateBy: "system",
UpdateTime: time.Now(),
UserID: 1,
DeptID: 1,
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},
}
// 获取第一个通道的配置

@ -47,6 +47,11 @@ func NewStreamCore() *StorageST {
}
debug = tmp.Server.Debug
// 同步数据库启用状态
if tmp.Server.DatabaseEnabled {
tmp.Database.Enabled = true
}
// 初始化数据库连接
if tmp.Database.Enabled {
err = tmp.InitDatabase()

@ -219,6 +219,38 @@ func (obj *StorageST) GetDatabaseStatus() map[string]interface{} {
return status
}
// DatabaseGetCameras 获取所有摄像头
func (obj *StorageST) DatabaseGetCameras() ([]Camera, error) {
obj.mutex.RLock()
defer obj.mutex.RUnlock()
if obj.dbManager == nil {
return nil, fmt.Errorf("database manager not initialized")
}
return obj.dbManager.GetAllCameras()
}
// DatabaseGetCamera 根据ID获取摄像头
func (obj *StorageST) DatabaseGetCamera(id string) (*Camera, error) {
obj.mutex.RLock()
defer obj.mutex.RUnlock()
if obj.dbManager == nil {
return nil, fmt.Errorf("database manager not initialized")
}
return obj.dbManager.GetCameraByID(id)
}
// StreamExist 检查流是否存在
func (obj *StorageST) StreamExist(uuid string) bool {
obj.mutex.RLock()
defer obj.mutex.RUnlock()
_, ok := obj.Streams[uuid]
return ok
}
// CloseDatabase 关闭数据库连接
func (obj *StorageST) CloseDatabase() error {
obj.mutex.Lock()

Loading…
Cancel
Save