feat(API): 新增按单位代码获取摄像头列表接口

新增/cameras/unit/:unitcode接口,支持根据单位代码过滤摄像头列表
适配qsc_camera表结构调整数据库模型和API响应格式
添加API使用文档说明各接口调用方式
ziyun-rtsp-web
karlkyo 8 months ago
parent 2a37fbf7be
commit bbccd98711

@ -0,0 +1,371 @@
# 摄像头管理 API 使用说明
本文档说明如何使用摄像头管理相关的 REST API 接口。
## 基础配置
### 服务器地址
```
http://localhost:8083 # 默认地址,根据实际配置调整
```
### 认证
如果启用了 HTTP 认证,需要在请求头中添加 Basic Auth
```
Authorization: Basic <base64(username:password)>
```
## API 接口列表
### 1. 获取所有摄像头列表
**请求方式:** `GET`
**请求路径:** `/cameras`
**描述:** 获取所有未删除的摄像头列表
**响应示例:**
```json
{
"cameras": [
{
"camera_id": 1,
"ip": "192.168.1.100",
"port": 554,
"username": "admin",
"password": "password",
"url": "rtsp://admin:password@192.168.1.100:554/stream1",
"camera_produce": "海康威视",
"camera_name": "前门摄像头",
"device_type": "网络摄像头",
"unit_code": "UNIT001",
"nvr_produce": "海康威视",
"nvr_path": "/stream1",
"play_back": "支持",
"del_flag": "0",
"create_by": "admin",
"create_time": "2024-01-01T10:00:00Z",
"update_by": "admin",
"update_time": "2024-01-01T10:00:00Z",
"user_id": 1,
"dept_id": 1,
"stream_status": "online"
}
],
"total": 1
}
```
### 2. 根据单位代码获取摄像头列表
**请求方式:** `GET`
**请求路径:** `/cameras/unit/{unitcode}`
**描述:** 根据单位代码获取对应的摄像头列表
**路径参数:**
- `unitcode`: 单位代码(必填)
**响应示例:**
```json
{
"cameras": [
{
"camera_id": 1,
"ip": "192.168.1.100",
"port": 554,
"username": "admin",
"password": "password",
"url": "rtsp://admin:password@192.168.1.100:554/stream1",
"camera_produce": "海康威视",
"camera_name": "前门摄像头",
"device_type": "网络摄像头",
"unit_code": "UNIT001",
"nvr_produce": "海康威视",
"nvr_path": "/stream1",
"play_back": "支持",
"del_flag": "0",
"create_by": "admin",
"create_time": "2024-01-01T10:00:00Z",
"update_by": "admin",
"update_time": "2024-01-01T10:00:00Z",
"user_id": 1,
"dept_id": 1,
"stream_status": "online"
}
],
"total": 1,
"unit_code": "UNIT001"
}
```
### 3. 其他摄像头管理接口
- `POST /camera/add` - 添加摄像头
- `GET /camera/{uuid}` - 获取单个摄像头信息
- `PUT /camera/{uuid}` - 更新摄像头信息
- `DELETE /camera/{uuid}` - 删除摄像头
- `POST /cameras/refresh` - 刷新摄像头列表
- `GET /database/status` - 获取数据库状态
## 前端调用示例
### JavaScript (原生)
```javascript
// 获取所有摄像头
async function getAllCameras() {
try {
const response = await fetch('http://localhost:8083/cameras', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
// 如果需要认证,添加以下头部
// 'Authorization': 'Basic ' + btoa('username:password')
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('所有摄像头:', data.cameras);
return data;
} catch (error) {
console.error('获取摄像头列表失败:', error);
}
}
// 根据单位代码获取摄像头
async function getCamerasByUnitCode(unitCode) {
try {
const response = await fetch(`http://localhost:8083/cameras/unit/${unitCode}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
// 如果需要认证,添加以下头部
// 'Authorization': 'Basic ' + btoa('username:password')
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log(`单位 ${unitCode} 的摄像头:`, data.cameras);
return data;
} catch (error) {
console.error('获取摄像头列表失败:', error);
}
}
// 使用示例
getAllCameras();
getCamerasByUnitCode('UNIT001');
```
### jQuery
```javascript
// 获取所有摄像头
function getAllCameras() {
$.ajax({
url: 'http://localhost:8083/cameras',
type: 'GET',
dataType: 'json',
// 如果需要认证
// beforeSend: function(xhr) {
// xhr.setRequestHeader('Authorization', 'Basic ' + btoa('username:password'));
// },
success: function(data) {
console.log('所有摄像头:', data.cameras);
// 处理数据
},
error: function(xhr, status, error) {
console.error('获取摄像头列表失败:', error);
}
});
}
// 根据单位代码获取摄像头
function getCamerasByUnitCode(unitCode) {
$.ajax({
url: `http://localhost:8083/cameras/unit/${unitCode}`,
type: 'GET',
dataType: 'json',
// 如果需要认证
// beforeSend: function(xhr) {
// xhr.setRequestHeader('Authorization', 'Basic ' + btoa('username:password'));
// },
success: function(data) {
console.log(`单位 ${unitCode} 的摄像头:`, data.cameras);
// 处理数据
},
error: function(xhr, status, error) {
console.error('获取摄像头列表失败:', error);
}
});
}
```
### Axios
```javascript
import axios from 'axios';
// 配置基础URL和认证
const api = axios.create({
baseURL: 'http://localhost:8083',
headers: {
'Content-Type': 'application/json',
// 如果需要认证
// 'Authorization': 'Basic ' + btoa('username:password')
}
});
// 获取所有摄像头
export const getAllCameras = async () => {
try {
const response = await api.get('/cameras');
return response.data;
} catch (error) {
console.error('获取摄像头列表失败:', error);
throw error;
}
};
// 根据单位代码获取摄像头
export const getCamerasByUnitCode = async (unitCode) => {
try {
const response = await api.get(`/cameras/unit/${unitCode}`);
return response.data;
} catch (error) {
console.error('获取摄像头列表失败:', error);
throw error;
}
};
```
### Vue.js 组件示例
```vue
<template>
<div>
<h2>摄像头管理</h2>
<!-- 单位代码选择 -->
<div>
<label>选择单位代码:</label>
<select v-model="selectedUnitCode" @change="loadCamerasByUnit">
<option value="">全部</option>
<option value="UNIT001">UNIT001</option>
<option value="UNIT002">UNIT002</option>
</select>
</div>
<!-- 摄像头列表 -->
<div v-if="loading">加载中...</div>
<div v-else>
<h3>摄像头列表 (共 {{ cameras.length }} 个)</h3>
<div v-for="camera in cameras" :key="camera.camera_id" class="camera-item">
<h4>{{ camera.camera_name }}</h4>
<p>IP: {{ camera.ip }}:{{ camera.port }}</p>
<p>单位代码: {{ camera.unit_code }}</p>
<p>状态: <span :class="camera.stream_status">{{ camera.stream_status }}</span></p>
</div>
</div>
</div>
</template>
<script>
import { getAllCameras, getCamerasByUnitCode } from './api';
export default {
name: 'CameraManager',
data() {
return {
cameras: [],
selectedUnitCode: '',
loading: false
};
},
mounted() {
this.loadAllCameras();
},
methods: {
async loadAllCameras() {
this.loading = true;
try {
const data = await getAllCameras();
this.cameras = data.cameras;
} catch (error) {
console.error('加载摄像头失败:', error);
} finally {
this.loading = false;
}
},
async loadCamerasByUnit() {
if (!this.selectedUnitCode) {
this.loadAllCameras();
return;
}
this.loading = true;
try {
const data = await getCamerasByUnitCode(this.selectedUnitCode);
this.cameras = data.cameras;
} catch (error) {
console.error('加载摄像头失败:', error);
} finally {
this.loading = false;
}
}
}
};
</script>
<style scoped>
.camera-item {
border: 1px solid #ddd;
padding: 10px;
margin: 10px 0;
border-radius: 5px;
}
.online {
color: green;
font-weight: bold;
}
.offline {
color: red;
font-weight: bold;
}
</style>
```
## 错误处理
### 常见错误码
- `400 Bad Request` - 请求参数错误
- `401 Unauthorized` - 认证失败
- `500 Internal Server Error` - 服务器内部错误
- `503 Service Unavailable` - 数据库未启用
### 错误响应格式
```json
{
"error": "错误描述信息"
}
```
## 注意事项
1. **数据库配置**:确保在 `config.json` 中正确配置了数据库连接信息
2. **认证**:如果启用了 HTTP 认证,所有请求都需要包含认证头部
3. **CORS**:如果前端和后端不在同一域名下,需要处理跨域问题
4. **流状态**`stream_status` 字段表示摄像头的实时流状态online/offline
5. **单位代码**`unit_code` 字段用于按组织单位过滤摄像头
6. **删除标记**:只返回 `del_flag != '1'` 的摄像头(未删除的摄像头)

Binary file not shown.

@ -1,11 +1,11 @@
package main
import (
"fmt"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
)
@ -19,21 +19,32 @@ type CameraRequest struct {
Enabled bool `json:"enabled"`
}
// CameraResponse 摄像头响应结构
// CameraResponse 摄像头响应结构 - 适配qsc_camera表
type CameraResponse struct {
ID string `json:"id"`
Name string `json:"name"`
CameraID int `json:"camera_id"`
IP string `json:"ip"`
Port int `json:"port"`
Username string `json:"username"`
Password string `json:"password,omitempty"` // 密码在响应中可选
RTSPURL string `json:"rtsp_url"`
Enabled bool `json:"enabled"`
Status string `json:"status"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Password string `json:"password,omitempty"`
URL string `json:"url"`
CameraProduce string `json:"camera_produce"`
CameraName string `json:"camera_name"`
DeviceType string `json:"device_type"`
UnitCode string `json:"unit_code"`
NvrProduce string `json:"nvr_produce"`
NvrPath string `json:"nvr_path"`
PlayBack string `json:"play_back"`
DelFlag string `json:"del_flag"`
CreateBy string `json:"create_by"`
CreateTime time.Time `json:"create_time"`
UpdateBy string `json:"update_by"`
UpdateTime time.Time `json:"update_time"`
UserID int64 `json:"user_id"`
DeptID int64 `json:"dept_id"`
StreamStatus string `json:"stream_status"`
}
// HTTPAPIServerCameras 获取所有摄像头列表
// HTTPAPIServerCameras 获取所有摄像头
func HTTPAPIServerCameras(c *gin.Context) {
if !Storage.Server.DatabaseEnabled {
c.JSON(http.StatusServiceUnavailable, gin.H{
@ -42,13 +53,6 @@ func HTTPAPIServerCameras(c *gin.Context) {
return
}
if Storage.dbManager == nil {
c.JSON(http.StatusInternalServerError, gin.H{
"error": "Database manager not initialized",
})
return
}
cameras, err := Storage.dbManager.GetAllCameras()
if err != nil {
log.WithFields(logrus.Fields{
@ -63,37 +67,121 @@ func HTTPAPIServerCameras(c *gin.Context) {
}
// 转换为响应格式
var response []CameraResponse
var responses []CameraResponse
for _, camera := range cameras {
status := "offline"
if stream, exists := Storage.Streams[camera.ID]; exists {
if len(stream.Channels) > 0 {
for _, channel := range stream.Channels {
if channel.runLock {
status = "online"
break
// 检查流状态
streamID := fmt.Sprintf("%d", camera.CameraID)
streamStatus := "offline"
if _, exists := Storage.Streams[streamID]; exists {
if Storage.StreamChannelExist(streamID, "0") {
streamStatus = "online"
}
}
response := CameraResponse{
CameraID: camera.CameraID,
IP: camera.IP,
Port: camera.Port,
Username: camera.Username,
Password: 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,
DelFlag: camera.DelFlag,
CreateBy: camera.CreateBy,
CreateTime: camera.CreateTime,
UpdateBy: camera.UpdateBy,
UpdateTime: camera.UpdateTime,
UserID: camera.UserID,
DeptID: camera.DeptID,
StreamStatus: streamStatus,
}
responses = append(responses, response)
}
c.JSON(http.StatusOK, gin.H{
"cameras": responses,
"total": len(responses),
})
}
// HTTPAPIServerCamerasByUnitCode 根据unitcode获取摄像头列表
func HTTPAPIServerCamerasByUnitCode(c *gin.Context) {
if !Storage.Server.DatabaseEnabled {
c.JSON(http.StatusServiceUnavailable, gin.H{
"error": "Database is not enabled",
})
return
}
unitCode := c.Param("unitcode")
if unitCode == "" {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Unit code is required",
})
return
}
cameras, err := Storage.dbManager.GetCamerasByUnitCode(unitCode)
if err != nil {
log.WithFields(logrus.Fields{
"module": "api",
"func": "HTTPAPIServerCamerasByUnitCode",
"call": "GetCamerasByUnitCode",
}).Errorln(err.Error())
c.JSON(http.StatusInternalServerError, gin.H{
"error": err.Error(),
})
return
}
// 转换为响应格式
var responses []CameraResponse
for _, camera := range cameras {
// 检查流状态
streamID := fmt.Sprintf("%d", camera.CameraID)
streamStatus := "offline"
if _, exists := Storage.Streams[streamID]; exists {
if Storage.StreamChannelExist(streamID, "0") {
streamStatus = "online"
}
}
response = append(response, CameraResponse{
ID: camera.ID,
Name: camera.Name,
response := CameraResponse{
CameraID: camera.CameraID,
IP: camera.IP,
Port: camera.Port,
Username: camera.Username,
// Password: camera.Password, // 不返回密码
RTSPURL: camera.RTSPURL,
Enabled: camera.Enabled,
Status: status,
CreatedAt: camera.CreatedAt,
UpdatedAt: camera.UpdatedAt,
})
Password: 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,
DelFlag: camera.DelFlag,
CreateBy: camera.CreateBy,
CreateTime: camera.CreateTime,
UpdateBy: camera.UpdateBy,
UpdateTime: camera.UpdateTime,
UserID: camera.UserID,
DeptID: camera.DeptID,
StreamStatus: streamStatus,
}
responses = append(responses, response)
}
c.JSON(http.StatusOK, gin.H{
"cameras": response,
"count": len(response),
"cameras": responses,
"total": len(responses),
"unit_code": unitCode,
})
}
@ -114,20 +202,23 @@ func HTTPAPIServerCameraAdd(c *gin.Context) {
return
}
// 生成新的UUID
cameraID := uuid.New().String()
// 创建摄像头记录
// 创建摄像头记录 - 适配qsc_camera表
camera := Camera{
ID: cameraID,
Name: req.Name,
IP: req.IP,
Port: 554, // 默认RTSP端口
Username: req.Username,
Password: req.Password,
RTSPURL: req.RTSPURL,
Enabled: req.Enabled,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
URL: req.RTSPURL,
CameraName: req.Name,
DeviceType: "网络摄像头",
UnitCode: "DEFAULT", // 默认单位代码
DelFlag: "0",
CreateBy: "admin",
CreateTime: time.Now(),
UpdateBy: "admin",
UpdateTime: time.Now(),
UserID: 1,
DeptID: 1,
}
// 保存到数据库
@ -144,9 +235,18 @@ func HTTPAPIServerCameraAdd(c *gin.Context) {
return
}
// 检查流是否已存在
streamID := fmt.Sprintf("%d", camera.CameraID)
if _, exists := Storage.Streams[streamID]; exists {
c.JSON(http.StatusConflict, gin.H{
"error": "Stream already exists",
})
return
}
// 转换为流配置并添加到内存
stream := CameraToStream(camera)
err = Storage.StreamAdd(cameraID, stream)
err = Storage.StreamAdd(streamID, stream)
if err != nil {
log.WithFields(logrus.Fields{
"module": "api",
@ -161,7 +261,7 @@ func HTTPAPIServerCameraAdd(c *gin.Context) {
c.JSON(http.StatusCreated, gin.H{
"message": "Camera added successfully",
"camera_id": cameraID,
"camera_id": camera.CameraID,
})
}
@ -211,18 +311,15 @@ func HTTPAPIServerCameraUpdate(c *gin.Context) {
return
}
// 更新摄像头信息
updatedCamera := Camera{
ID: cameraID,
Name: req.Name,
IP: req.IP,
Username: req.Username,
Password: req.Password,
RTSPURL: req.RTSPURL,
Enabled: req.Enabled,
CreatedAt: existingCamera.CreatedAt,
UpdatedAt: time.Now(),
}
// 更新摄像头信息 - 适配qsc_camera表
updatedCamera := *existingCamera // 复制现有摄像头信息
updatedCamera.IP = req.IP
updatedCamera.Username = req.Username
updatedCamera.Password = req.Password
updatedCamera.URL = req.RTSPURL
updatedCamera.CameraName = req.Name
updatedCamera.UpdateBy = "admin"
updatedCamera.UpdateTime = time.Now()
// 更新数据库
err = Storage.dbManager.UpdateCamera(&updatedCamera)
@ -354,7 +451,8 @@ func HTTPAPIServerCameraGet(c *gin.Context) {
// 检查流状态
status := "offline"
if stream, exists := Storage.Streams[camera.ID]; exists {
streamID := fmt.Sprintf("%d", camera.CameraID)
if stream, exists := Storage.Streams[streamID]; exists {
if len(stream.Channels) > 0 {
for _, channel := range stream.Channels {
if channel.runLock {
@ -366,16 +464,20 @@ func HTTPAPIServerCameraGet(c *gin.Context) {
}
response := CameraResponse{
ID: camera.ID,
Name: camera.Name,
CameraID: camera.CameraID,
CameraName: camera.CameraName,
IP: camera.IP,
Port: camera.Port,
Username: camera.Username,
// Password: camera.Password, // 不返回密码
RTSPURL: camera.RTSPURL,
Enabled: camera.Enabled,
Status: status,
CreatedAt: camera.CreatedAt,
UpdatedAt: camera.UpdatedAt,
URL: camera.URL,
CameraProduce: camera.CameraProduce,
DeviceType: camera.DeviceType,
UnitCode: camera.UnitCode,
NvrProduce: camera.NvrProduce,
StreamStatus: status,
CreateTime: camera.CreateTime,
UpdateTime: camera.UpdateTime,
}
c.JSON(http.StatusOK, response)

@ -91,6 +91,7 @@ func HTTPAPIServer() {
*/
privat.GET("/cameras", HTTPAPIServerCameras)
privat.GET("/cameras/unit/:unitcode", HTTPAPIServerCamerasByUnitCode)
privat.POST("/camera/add", HTTPAPIServerCameraAdd)
privat.GET("/camera/:uuid", HTTPAPIServerCameraGet)
privat.PUT("/camera/:uuid", HTTPAPIServerCameraUpdate)

@ -1,5 +1,6 @@
{
"server": {
"database_enabled": true,
"debug": true,
"http_debug": false,
"http_demo": true,
@ -24,25 +25,13 @@
}
},
"database": {
"enabled": false,
"enabled": true,
"type": "mysql",
"host": "localhost",
"port": 3306,
"username": "root",
"password": "password",
"database": "rtsp_cameras",
"table_name": "cameras"
},
"streams": {
"27aec28e-6181-4753-9acd-0456a75f0289": {
"channels": {
"0": {
"url": "rtmp://171.25.232.10/12d525bc9f014e209c1280bc0d46a87e",
"debug": false,
"audio": true
}
},
"name": "111111111"
}
"password": "123456",
"database": "ziyun",
"table_name": "qsc_camera"
}
}

@ -3,11 +3,11 @@ package main
import (
"database/sql"
"fmt"
"strconv"
"time"
_ "github.com/denisenkom/go-mssqldb"
_ "github.com/go-sql-driver/mysql"
"github.com/google/uuid"
)
// DatabaseConfig 数据库配置结构
@ -22,20 +22,28 @@ type DatabaseConfig struct {
SSLMode string `json:"ssl_mode,omitempty"` // for mysql
}
// Camera 摄像头数据库模型
// Camera 摄像头数据库模型 - 适配qsc_camera表
type Camera struct {
ID string `json:"id" db:"id"`
Name string `json:"name" db:"name"`
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"`
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"`
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"`
}
// DatabaseManager 数据库管理器
@ -139,11 +147,14 @@ func (dm *DatabaseManager) initTables() error {
return err
}
// GetAllCameras 获取所有摄像头
// GetAllCameras 获取所有摄像头 - 适配qsc_camera表
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 = ?`
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, true)
rows, err := dm.db.Query(query)
if err != nil {
return nil, err
}
@ -152,9 +163,11 @@ func (dm *DatabaseManager) GetAllCameras() ([]Camera, error) {
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)
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
}
@ -164,14 +177,49 @@ func (dm *DatabaseManager) GetAllCameras() ([]Camera, error) {
return cameras, nil
}
// GetCameraByID 根据ID获取摄像头
// GetCamerasByUnitCode 根据unitcode获取摄像头列表 - 适配qsc_camera表
func (dm *DatabaseManager) GetCamerasByUnitCode(unitCode 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 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
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 id, name, ip, username, password, rtsp_url, enabled, on_demand, audio, debug, created_at, updated_at FROM cameras WHERE id = ?`
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.ID, &camera.Name, &camera.IP,
&camera.Username, &camera.Password, &camera.RTSPURL, &camera.Enabled,
&camera.OnDemand, &camera.Audio, &camera.Debug, &camera.CreatedAt, &camera.UpdatedAt)
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 {
@ -183,41 +231,44 @@ func (dm *DatabaseManager) GetCameraByID(id string) (*Camera, error) {
return &camera, nil
}
// CreateCamera 创建摄像头
// CreateCamera 创建摄像头 - 适配qsc_camera表
func (dm *DatabaseManager) CreateCamera(camera *Camera) error {
if camera.ID == "" {
camera.ID = uuid.New().String()
}
camera.CreatedAt = time.Now()
camera.UpdatedAt = time.Now()
camera.CreateTime = time.Now()
camera.UpdateTime = time.Now()
query := `INSERT INTO cameras (id, name, ip, username, password, rtsp_url, enabled, on_demand, audio, debug, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
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.ID, camera.Name, camera.IP, camera.Username,
camera.Password, camera.RTSPURL, camera.Enabled, camera.OnDemand,
camera.Audio, camera.Debug, camera.CreatedAt, camera.UpdatedAt)
_, 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 更新摄像头
// UpdateCamera 更新摄像头 - 适配qsc_camera表
func (dm *DatabaseManager) UpdateCamera(camera *Camera) error {
camera.UpdatedAt = time.Now()
camera.UpdateTime = time.Now()
query := `UPDATE cameras SET name=?, ip=?, username=?, password=?, rtsp_url=?,
enabled=?, on_demand=?, audio=?, debug=?, updated_at=? WHERE id=?`
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.Name, camera.IP, camera.Username,
camera.Password, camera.RTSPURL, camera.Enabled, camera.OnDemand,
camera.Audio, camera.Debug, camera.UpdatedAt, 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 删除摄像头
// DeleteCamera 删除摄像头 - 适配qsc_camera表
func (dm *DatabaseManager) DeleteCamera(id string) error {
query := `DELETE FROM cameras WHERE id = ?`
query := `UPDATE qsc_camera SET del_flag = '1' WHERE camera_id = ?`
_, err := dm.db.Exec(query, id)
return err
}
@ -230,39 +281,61 @@ func (dm *DatabaseManager) Close() error {
return nil
}
// CameraToStream 将摄像头转换为流配置
// CameraToStream 将摄像头转换为流配置 - 适配qsc_camera表
func CameraToStream(camera Camera) StreamST {
// 构建RTSP URL
rtspURL := camera.URL
if rtspURL == "" {
// 如果URL为空根据IP、端口、用户名密码构建RTSP URL
if camera.Username != "" && camera.Password != "" {
rtspURL = fmt.Sprintf("rtsp://%s:%s@%s:%d/stream1",
camera.Username, camera.Password, camera.IP, camera.Port)
} else {
rtspURL = fmt.Sprintf("rtsp://%s:%d/stream1", camera.IP, camera.Port)
}
}
return StreamST{
Name: camera.Name,
Name: camera.CameraName,
Channels: map[string]ChannelST{
"0": {
Name: camera.Name,
URL: camera.RTSPURL,
OnDemand: camera.OnDemand,
Debug: camera.Debug,
Audio: camera.Audio,
Name: camera.CameraName,
URL: rtspURL,
OnDemand: false, // 根据需要设置
Debug: false, // 根据需要设置
Audio: true, // 根据需要设置
},
},
}
}
// StreamToCamera 将流配置转换为摄像头
// StreamToCamera 将流配置转换为摄像头 - 适配qsc_camera表
func StreamToCamera(id string, stream StreamST) Camera {
// 创建基础摄像头对象 - 适配qsc_camera表
cameraID, _ := strconv.Atoi(id)
camera := Camera{
ID: id,
Name: stream.Name,
Enabled: true,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
CameraID: cameraID,
CameraName: stream.Name,
IP: "", // 从URL中解析
Port: 554, // 默认RTSP端口
Username: "", // 从URL中解析
Password: "", // 从URL中解析
URL: "", // 将从第一个通道获取
DeviceType: "网络摄像头",
UnitCode: "DEFAULT",
DelFlag: "0",
CreateBy: "system",
CreateTime: time.Now(),
UpdateBy: "system",
UpdateTime: time.Now(),
UserID: 1,
DeptID: 1,
}
// 获取第一个通道的配置
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
camera.URL = channel.URL
break
}
}

@ -0,0 +1,64 @@
package main
import (
"database/sql"
"fmt"
"os"
_ "github.com/go-sql-driver/mysql"
"github.com/sirupsen/logrus"
)
var db *sql.DB
type DBConfig struct {
Host string `json:"host"`
Port int `json:"port"`
User string `json:"user"`
Password string `json:"password"`
Database string `json:"database"`
}
func initDB() error {
cfg := DBConfig{
Host: os.Getenv("DB_HOST"),
Port: 3306,
User: os.Getenv("DB_USER"),
Password: os.Getenv("DB_PASSWORD"),
Database: os.Getenv("DB_NAME"),
}
if cfg.Host == "" {
cfg.Host = "localhost"
}
if cfg.User == "" {
return fmt.Errorf("database user not configured")
}
if cfg.Password == "" {
return fmt.Errorf("database password not configured")
}
if cfg.Database == "" {
cfg.Database = "rtsp_config"
}
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?parseTime=true",
cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.Database)
var err error
db, err = sql.Open("mysql", dsn)
if err != nil {
return fmt.Errorf("failed to connect to database: %v", err)
}
err = db.Ping()
if err != nil {
return fmt.Errorf("failed to ping database: %v", err)
}
log.WithFields(logrus.Fields{
"module": "database",
"func": "initDB",
}).Info("Database connection established")
return nil
}

@ -66,12 +66,13 @@ func (obj *StorageST) LoadStreamsFromDatabase() error {
// 将摄像头转换为流配置
for _, camera := range cameras {
stream := CameraToStream(camera)
obj.Streams[camera.ID] = stream
streamID := fmt.Sprintf("%d", camera.CameraID)
obj.Streams[streamID] = stream
log.WithFields(logrus.Fields{
"module": "database",
"func": "LoadStreamsFromDatabase",
"camera": camera.Name,
"id": camera.ID,
"camera": camera.CameraName,
"id": camera.CameraID,
}).Infoln("Loaded camera from database")
}
@ -114,11 +115,11 @@ func (obj *StorageST) SyncStreamToDatabase(streamID string, stream StreamST) err
"module": "database",
"func": "SyncStreamToDatabase",
"id": streamID,
"name": camera.Name,
"name": camera.CameraName,
}).Infoln("Created camera in database")
} else {
// 更新现有摄像头
camera.CreatedAt = existingCamera.CreatedAt // 保持创建时间
camera.CreateTime = existingCamera.CreateTime // 保持创建时间
err = obj.dbManager.UpdateCamera(&camera)
if err != nil {
log.WithFields(logrus.Fields{
@ -133,7 +134,7 @@ func (obj *StorageST) SyncStreamToDatabase(streamID string, stream StreamST) err
"module": "database",
"func": "SyncStreamToDatabase",
"id": streamID,
"name": camera.Name,
"name": camera.CameraName,
}).Infoln("Updated camera in database")
}

Loading…
Cancel
Save