feat(database): 为Camera结构体添加Enabled字段并实现兼容查询

refactor(web): 增强摄像头管理表单和卡片显示功能

移除Java集成示例代码,专注于Go实现
```

这个提交消息:
1. 使用中文描述变更内容
2. 分为三个主要部分:
   - 数据库功能增强:添加Enabled字段并实现兼容查询
   - 前端重构:改进摄像头管理界面
   - 清理:移除不再需要的Java示例代码
3. 遵循了类型+简要描述的格式
4. 对数据库变更使用了feat类型,对界面改进使用了refactor类型
5. 保持了简洁性,同时涵盖了主要变更点
ziyun-rtsp-web
ytx@queuingsystem.cn 8 months ago
parent 1a617370d6
commit 575fe52b9a

Binary file not shown.

@ -37,6 +37,7 @@ type Camera struct {
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"`
@ -147,29 +148,53 @@ func (dm *DatabaseManager) initTables() error {
return err
}
// GetAllCameras 获取所有摄像头 - 适配qsc_camera表
// 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,
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 {
return nil, err
// 如果查询失败可能是因为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
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
// 检查列数来决定如何扫描
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)
}
@ -179,27 +204,51 @@ func (dm *DatabaseManager) GetAllCameras() ([]Camera, error) {
// 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,
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 {
return nil, err
// 如果查询失败可能是因为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
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
// 检查列数来决定如何扫描
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)
}

@ -1,342 +0,0 @@
package com.example.rtspweb;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.client.HttpClientErrorException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* RTSPtoWeb Java
* RTSPtoWeb GoAPI
*/
public class RTSPWebClient {
private final String baseUrl;
private final RestTemplate restTemplate;
private final ObjectMapper objectMapper;
public RTSPWebClient(String baseUrl) {
this.baseUrl = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl;
this.restTemplate = new RestTemplate();
this.objectMapper = new ObjectMapper();
}
/**
*
*/
public ApiResponse<CameraListResponse> getCameras(String unitCode, String status, int page, int size) {
try {
String url = String.format("%s/api/java/cameras?page=%d&size=%d", baseUrl, page, size);
if (unitCode != null && !unitCode.isEmpty()) {
url += "&unit_code=" + unitCode;
}
if (status != null && !status.isEmpty()) {
url += "&status=" + status;
}
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
TypeReference<ApiResponse<CameraListResponse>> typeRef = new TypeReference<ApiResponse<CameraListResponse>>() {};
return objectMapper.readValue(response.getBody(), typeRef);
} catch (Exception e) {
return createErrorResponse("获取摄像头列表失败: " + e.getMessage());
}
}
/**
*
*/
public ApiResponse<StreamListResponse> getStreams(int page, int size) {
try {
String url = String.format("%s/api/java/streams?page=%d&size=%d", baseUrl, page, size);
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
TypeReference<ApiResponse<StreamListResponse>> typeRef = new TypeReference<ApiResponse<StreamListResponse>>() {};
return objectMapper.readValue(response.getBody(), typeRef);
} catch (Exception e) {
return createErrorResponse("获取流列表失败: " + e.getMessage());
}
}
/**
*
*/
public ApiResponse<Camera> getCameraDetail(String cameraId) {
try {
String url = String.format("%s/api/java/camera/%s", baseUrl, cameraId);
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
TypeReference<ApiResponse<Camera>> typeRef = new TypeReference<ApiResponse<Camera>>() {};
return objectMapper.readValue(response.getBody(), typeRef);
} catch (HttpClientErrorException e) {
if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
return createErrorResponse("摄像头不存在");
}
return createErrorResponse("获取摄像头详情失败: " + e.getMessage());
} catch (Exception e) {
return createErrorResponse("获取摄像头详情失败: " + e.getMessage());
}
}
/**
*
*/
public ApiResponse<SystemInfo> getSystemInfo() {
try {
String url = String.format("%s/api/java/system/info", baseUrl);
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
TypeReference<ApiResponse<SystemInfo>> typeRef = new TypeReference<ApiResponse<SystemInfo>>() {};
return objectMapper.readValue(response.getBody(), typeRef);
} catch (Exception e) {
return createErrorResponse("获取系统信息失败: " + e.getMessage());
}
}
/**
*
*/
public ApiResponse<Camera> addCamera(Camera camera) {
try {
String url = String.format("%s/camera/add", baseUrl);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Camera> request = new HttpEntity<>(camera, headers);
ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
TypeReference<ApiResponse<Camera>> typeRef = new TypeReference<ApiResponse<Camera>>() {};
return objectMapper.readValue(response.getBody(), typeRef);
} catch (Exception e) {
return createErrorResponse("添加摄像头失败: " + e.getMessage());
}
}
/**
*
*/
public ApiResponse<Object> refreshCameras() {
try {
String url = String.format("%s/cameras/refresh", baseUrl);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> request = new HttpEntity<>("{}", headers);
ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
TypeReference<ApiResponse<Object>> typeRef = new TypeReference<ApiResponse<Object>>() {};
return objectMapper.readValue(response.getBody(), typeRef);
} catch (Exception e) {
return createErrorResponse("刷新摄像头状态失败: " + e.getMessage());
}
}
private <T> ApiResponse<T> createErrorResponse(String message) {
ApiResponse<T> response = new ApiResponse<>();
response.setCode(500);
response.setMessage(message);
response.setSuccess(false);
return response;
}
// 数据模型类
public static class ApiResponse<T> {
private int code;
private String message;
private T data;
private boolean success;
// Getters and Setters
public int getCode() { return code; }
public void setCode(int code) { this.code = code; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public T getData() { return data; }
public void setData(T data) { this.data = data; }
public boolean isSuccess() { return success; }
public void setSuccess(boolean success) { this.success = success; }
}
public static class CameraListResponse {
private int total;
private List<Camera> cameras;
public int getTotal() { return total; }
public void setTotal(int total) { this.total = total; }
public List<Camera> getCameras() { return cameras; }
public void setCameras(List<Camera> cameras) { this.cameras = cameras; }
}
public static class StreamListResponse {
private int total;
private List<Stream> streams;
public int getTotal() { return total; }
public void setTotal(int total) { this.total = total; }
public List<Stream> getStreams() { return streams; }
public void setStreams(List<Stream> streams) { this.streams = streams; }
}
public static class Camera {
private String id;
private String name;
private String ip;
private int port;
private String username;
private String password;
@JsonProperty("rtsp_url")
private String rtspUrl;
private String status;
private boolean enabled;
@JsonProperty("device_type")
private String deviceType;
@JsonProperty("unit_code")
private String unitCode;
@JsonProperty("created_at")
private LocalDateTime createdAt;
@JsonProperty("updated_at")
private LocalDateTime updatedAt;
@JsonProperty("play_urls")
private PlayUrls playUrls;
// Getters and Setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getIp() { return ip; }
public void setIp(String ip) { this.ip = ip; }
public int getPort() { return port; }
public void setPort(int port) { this.port = port; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getRtspUrl() { return rtspUrl; }
public void setRtspUrl(String rtspUrl) { this.rtspUrl = rtspUrl; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public String getDeviceType() { return deviceType; }
public void setDeviceType(String deviceType) { this.deviceType = deviceType; }
public String getUnitCode() { return unitCode; }
public void setUnitCode(String unitCode) { this.unitCode = unitCode; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
public PlayUrls getPlayUrls() { return playUrls; }
public void setPlayUrls(PlayUrls playUrls) { this.playUrls = playUrls; }
}
public static class Stream {
private String id;
private String name;
private Map<String, String> channels;
@JsonProperty("play_urls")
private PlayUrls playUrls;
// Getters and Setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Map<String, String> getChannels() { return channels; }
public void setChannels(Map<String, String> channels) { this.channels = channels; }
public PlayUrls getPlayUrls() { return playUrls; }
public void setPlayUrls(PlayUrls playUrls) { this.playUrls = playUrls; }
}
public static class PlayUrls {
private String hls;
private String webrtc;
private String mse;
private String all;
// Getters and Setters
public String getHls() { return hls; }
public void setHls(String hls) { this.hls = hls; }
public String getWebrtc() { return webrtc; }
public void setWebrtc(String webrtc) { this.webrtc = webrtc; }
public String getMse() { return mse; }
public void setMse(String mse) { this.mse = mse; }
public String getAll() { return all; }
public void setAll(String all) { this.all = all; }
}
public static class SystemInfo {
private String version;
@JsonProperty("database_enabled")
private boolean databaseEnabled;
@JsonProperty("demo_enabled")
private boolean demoEnabled;
@JsonProperty("total_streams")
private int totalStreams;
@JsonProperty("total_cameras")
private int totalCameras;
@JsonProperty("online_cameras")
private int onlineCameras;
@JsonProperty("server_time")
private String serverTime;
// Getters and Setters
public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }
public boolean isDatabaseEnabled() { return databaseEnabled; }
public void setDatabaseEnabled(boolean databaseEnabled) { this.databaseEnabled = databaseEnabled; }
public boolean isDemoEnabled() { return demoEnabled; }
public void setDemoEnabled(boolean demoEnabled) { this.demoEnabled = demoEnabled; }
public int getTotalStreams() { return totalStreams; }
public void setTotalStreams(int totalStreams) { this.totalStreams = totalStreams; }
public int getTotalCameras() { return totalCameras; }
public void setTotalCameras(int totalCameras) { this.totalCameras = totalCameras; }
public int getOnlineCameras() { return onlineCameras; }
public void setOnlineCameras(int onlineCameras) { this.onlineCameras = onlineCameras; }
public String getServerTime() { return serverTime; }
public void setServerTime(String serverTime) { this.serverTime = serverTime; }
}
}

@ -1,252 +0,0 @@
package com.example.rtspweb;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;
/**
* RTSPtoWeb
* WebREST API
*/
@Controller
@RequestMapping("/rtsp")
public class RTSPWebController {
@Value("${rtspweb.server.url:http://localhost:8083}")
private String rtspWebServerUrl;
private RTSPWebClient rtspWebClient;
@PostConstruct
public void init() {
this.rtspWebClient = new RTSPWebClient(rtspWebServerUrl);
}
/**
*
*/
@GetMapping("/cameras")
public String camerasPage(Model model) {
model.addAttribute("serverUrl", rtspWebServerUrl);
return "rtsp/cameras";
}
/**
*
*/
@GetMapping("/streams")
public String streamsPage(Model model) {
model.addAttribute("serverUrl", rtspWebServerUrl);
return "rtsp/streams";
}
/**
*
*/
@GetMapping("/player/{cameraId}")
public String playerPage(@PathVariable String cameraId, Model model) {
RTSPWebClient.ApiResponse<RTSPWebClient.Camera> response = rtspWebClient.getCameraDetail(cameraId);
if (response.isSuccess()) {
model.addAttribute("camera", response.getData());
model.addAttribute("serverUrl", rtspWebServerUrl);
return "rtsp/player";
} else {
model.addAttribute("error", response.getMessage());
return "rtsp/error";
}
}
/**
* iframe使
*/
@GetMapping("/embed/player/{cameraId}")
public String embedPlayerPage(@PathVariable String cameraId,
@RequestParam(defaultValue = "hls") String type,
Model model) {
RTSPWebClient.ApiResponse<RTSPWebClient.Camera> response = rtspWebClient.getCameraDetail(cameraId);
if (response.isSuccess()) {
RTSPWebClient.Camera camera = response.getData();
String playUrl = getPlayUrl(camera, type);
model.addAttribute("camera", camera);
model.addAttribute("playUrl", playUrl);
model.addAttribute("playType", type);
model.addAttribute("serverUrl", rtspWebServerUrl);
return "rtsp/embed-player";
} else {
model.addAttribute("error", response.getMessage());
return "rtsp/error";
}
}
// REST API 接口
/**
* API
*/
@GetMapping("/api/cameras")
@ResponseBody
public ResponseEntity<RTSPWebClient.ApiResponse<RTSPWebClient.CameraListResponse>> getCameras(
@RequestParam(required = false) String unitCode,
@RequestParam(required = false) String status,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "20") int size) {
RTSPWebClient.ApiResponse<RTSPWebClient.CameraListResponse> response =
rtspWebClient.getCameras(unitCode, status, page, size);
return ResponseEntity.ok(response);
}
/**
* API
*/
@GetMapping("/api/streams")
@ResponseBody
public ResponseEntity<RTSPWebClient.ApiResponse<RTSPWebClient.StreamListResponse>> getStreams(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "20") int size) {
RTSPWebClient.ApiResponse<RTSPWebClient.StreamListResponse> response =
rtspWebClient.getStreams(page, size);
return ResponseEntity.ok(response);
}
/**
* API
*/
@GetMapping("/api/camera/{cameraId}")
@ResponseBody
public ResponseEntity<RTSPWebClient.ApiResponse<RTSPWebClient.Camera>> getCameraDetail(
@PathVariable String cameraId) {
RTSPWebClient.ApiResponse<RTSPWebClient.Camera> response =
rtspWebClient.getCameraDetail(cameraId);
return ResponseEntity.ok(response);
}
/**
* API
*/
@GetMapping("/api/system/info")
@ResponseBody
public ResponseEntity<RTSPWebClient.ApiResponse<RTSPWebClient.SystemInfo>> getSystemInfo() {
RTSPWebClient.ApiResponse<RTSPWebClient.SystemInfo> response =
rtspWebClient.getSystemInfo();
return ResponseEntity.ok(response);
}
/**
* API
*/
@PostMapping("/api/camera")
@ResponseBody
public ResponseEntity<RTSPWebClient.ApiResponse<RTSPWebClient.Camera>> addCamera(
@RequestBody RTSPWebClient.Camera camera) {
RTSPWebClient.ApiResponse<RTSPWebClient.Camera> response =
rtspWebClient.addCamera(camera);
return ResponseEntity.ok(response);
}
/**
* API
*/
@PostMapping("/api/cameras/refresh")
@ResponseBody
public ResponseEntity<RTSPWebClient.ApiResponse<Object>> refreshCameras() {
RTSPWebClient.ApiResponse<Object> response = rtspWebClient.refreshCameras();
return ResponseEntity.ok(response);
}
/**
* API
*/
@GetMapping("/api/camera/{cameraId}/play-url")
@ResponseBody
public ResponseEntity<Map<String, Object>> getPlayUrl(
@PathVariable String cameraId,
@RequestParam(defaultValue = "hls") String type) {
Map<String, Object> result = new HashMap<>();
RTSPWebClient.ApiResponse<RTSPWebClient.Camera> response =
rtspWebClient.getCameraDetail(cameraId);
if (response.isSuccess()) {
RTSPWebClient.Camera camera = response.getData();
String playUrl = getPlayUrl(camera, type);
result.put("success", true);
result.put("playUrl", playUrl);
result.put("type", type);
result.put("camera", camera);
} else {
result.put("success", false);
result.put("message", response.getMessage());
}
return ResponseEntity.ok(result);
}
/**
* API
*/
@GetMapping("/api/health")
@ResponseBody
public ResponseEntity<Map<String, Object>> healthCheck() {
Map<String, Object> result = new HashMap<>();
try {
RTSPWebClient.ApiResponse<RTSPWebClient.SystemInfo> response =
rtspWebClient.getSystemInfo();
if (response.isSuccess()) {
result.put("status", "UP");
result.put("rtspWebServer", "CONNECTED");
result.put("serverUrl", rtspWebServerUrl);
result.put("systemInfo", response.getData());
} else {
result.put("status", "DOWN");
result.put("rtspWebServer", "DISCONNECTED");
result.put("error", response.getMessage());
}
} catch (Exception e) {
result.put("status", "DOWN");
result.put("rtspWebServer", "ERROR");
result.put("error", e.getMessage());
}
return ResponseEntity.ok(result);
}
// 工具方法
private String getPlayUrl(RTSPWebClient.Camera camera, String type) {
if (camera.getPlayUrls() == null) {
return "";
}
switch (type.toLowerCase()) {
case "hls":
return camera.getPlayUrls().getHls();
case "webrtc":
return camera.getPlayUrls().getWebrtc();
case "mse":
return camera.getPlayUrls().getMse();
case "all":
default:
return camera.getPlayUrls().getAll();
}
}
}

@ -79,26 +79,91 @@
</div>
<div class="modal-body">
<form id="addCameraForm">
<div class="form-floating">
<input type="text" class="form-control" id="addName" placeholder="摄像头名称" required>
<label for="addName">摄像头名称</label>
</div>
<div class="form-floating">
<input type="text" class="form-control" id="addIP" placeholder="IP地址" required>
<label for="addIP">IP地址</label>
<div class="row">
<div class="col-md-8">
<div class="form-floating">
<input type="text" class="form-control" id="addCameraName" placeholder="摄像头名称" required>
<label for="addCameraName">摄像头名称</label>
</div>
</div>
<div class="col-md-4">
<div class="form-floating">
<input type="text" class="form-control" id="addUnitCode" placeholder="单位代码">
<label for="addUnitCode">单位代码</label>
</div>
</div>
</div>
<div class="form-floating">
<input type="text" class="form-control" id="addUsername" placeholder="用户名">
<label for="addUsername">用户名</label>
<div class="row">
<div class="col-md-8">
<div class="form-floating">
<input type="text" class="form-control" id="addIP" placeholder="IP地址" required>
<label for="addIP">IP地址</label>
</div>
</div>
<div class="col-md-4">
<div class="form-floating">
<input type="number" class="form-control" id="addPort" placeholder="端口" value="554" required>
<label for="addPort">端口</label>
</div>
</div>
</div>
<div class="form-floating">
<input type="password" class="form-control" id="addPassword" placeholder="密码">
<label for="addPassword">密码</label>
<div class="row">
<div class="col-md-6">
<div class="form-floating">
<input type="text" class="form-control" id="addUsername" placeholder="用户名">
<label for="addUsername">用户名</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating">
<input type="password" class="form-control" id="addPassword" placeholder="密码">
<label for="addPassword">密码</label>
</div>
</div>
</div>
<div class="form-floating">
<input type="text" class="form-control" id="addRTSPURL" placeholder="RTSP URL" required>
<label for="addRTSPURL">RTSP URL</label>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-floating">
<select class="form-select" id="addDeviceType">
<option value="">选择设备类型</option>
<option value="网络摄像头">网络摄像头</option>
<option value="球机">球机</option>
<option value="枪机">枪机</option>
<option value="半球">半球</option>
<option value="其他">其他</option>
</select>
<label for="addDeviceType">设备类型</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating">
<input type="text" class="form-control" id="addCameraProduce" placeholder="摄像头厂商">
<label for="addCameraProduce">摄像头厂商</label>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-floating">
<input type="text" class="form-control" id="addNvrProduce" placeholder="NVR厂商">
<label for="addNvrProduce">NVR厂商</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating">
<input type="text" class="form-control" id="addNvrPath" placeholder="NVR路径">
<label for="addNvrPath">NVR路径</label>
</div>
</div>
</div>
<div class="form-floating">
<input type="text" class="form-control" id="addPlayBack" placeholder="回放地址">
<label for="addPlayBack">回放地址</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="addEnabled" checked>
<label class="form-check-label" for="addEnabled">
@ -126,26 +191,91 @@
<div class="modal-body">
<form id="editCameraForm">
<input type="hidden" id="editCameraId">
<div class="form-floating">
<input type="text" class="form-control" id="editName" placeholder="摄像头名称" required>
<label for="editName">摄像头名称</label>
</div>
<div class="form-floating">
<input type="text" class="form-control" id="editIP" placeholder="IP地址" required>
<label for="editIP">IP地址</label>
<div class="row">
<div class="col-md-8">
<div class="form-floating">
<input type="text" class="form-control" id="editCameraName" placeholder="摄像头名称" required>
<label for="editCameraName">摄像头名称</label>
</div>
</div>
<div class="col-md-4">
<div class="form-floating">
<input type="text" class="form-control" id="editUnitCode" placeholder="单位代码">
<label for="editUnitCode">单位代码</label>
</div>
</div>
</div>
<div class="form-floating">
<input type="text" class="form-control" id="editUsername" placeholder="用户名">
<label for="editUsername">用户名</label>
<div class="row">
<div class="col-md-8">
<div class="form-floating">
<input type="text" class="form-control" id="editIP" placeholder="IP地址" required>
<label for="editIP">IP地址</label>
</div>
</div>
<div class="col-md-4">
<div class="form-floating">
<input type="number" class="form-control" id="editPort" placeholder="端口" required>
<label for="editPort">端口</label>
</div>
</div>
</div>
<div class="form-floating">
<input type="password" class="form-control" id="editPassword" placeholder="密码">
<label for="editPassword">密码</label>
<div class="row">
<div class="col-md-6">
<div class="form-floating">
<input type="text" class="form-control" id="editUsername" placeholder="用户名">
<label for="editUsername">用户名</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating">
<input type="password" class="form-control" id="editPassword" placeholder="密码">
<label for="editPassword">密码</label>
</div>
</div>
</div>
<div class="form-floating">
<input type="text" class="form-control" id="editRTSPURL" placeholder="RTSP URL" required>
<label for="editRTSPURL">RTSP URL</label>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-floating">
<select class="form-select" id="editDeviceType">
<option value="">选择设备类型</option>
<option value="网络摄像头">网络摄像头</option>
<option value="球机">球机</option>
<option value="枪机">枪机</option>
<option value="半球">半球</option>
<option value="其他">其他</option>
</select>
<label for="editDeviceType">设备类型</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating">
<input type="text" class="form-control" id="editCameraProduce" placeholder="摄像头厂商">
<label for="editCameraProduce">摄像头厂商</label>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-floating">
<input type="text" class="form-control" id="editNvrProduce" placeholder="NVR厂商">
<label for="editNvrProduce">NVR厂商</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating">
<input type="text" class="form-control" id="editNvrPath" placeholder="NVR路径">
<label for="editNvrPath">NVR路径</label>
</div>
</div>
</div>
<div class="form-floating">
<input type="text" class="form-control" id="editPlayBack" placeholder="回放地址">
<label for="editPlayBack">回放地址</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="editEnabled">
<label class="form-check-label" for="editEnabled">
@ -221,8 +351,8 @@
container.innerHTML = '';
cameras.forEach(camera => {
const statusClass = camera.status === 'online' ? 'status-online' : 'status-offline';
const statusIcon = camera.status === 'online' ? 'fas fa-circle' : 'far fa-circle';
const statusClass = camera.stream_status === 'online' ? 'status-online' : 'status-offline';
const statusIcon = camera.stream_status === 'online' ? 'fas fa-circle' : 'far fa-circle';
const enabledBadge = camera.enabled ?
'<span class="badge bg-success">已启用</span>' :
'<span class="badge bg-secondary">已禁用</span>';
@ -231,30 +361,32 @@
<div class="col-md-6 col-lg-4 mb-3">
<div class="card camera-card h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="mb-0">${camera.name}</h6>
<h6 class="mb-0">${camera.camera_name || camera.name || 'undefined'}</h6>
${enabledBadge}
</div>
<div class="card-body">
<p class="card-text">
<strong>IP:</strong> ${camera.ip}<br>
<strong>端口:</strong> ${camera.port || 554}<br>
<strong>用户名:</strong> ${camera.username || ''}<br>
<strong>设备类型:</strong> ${camera.device_type || '未知'}<br>
<strong>状态:</strong>
<i class="${statusIcon} ${statusClass}"></i>
<span class="${statusClass}">${camera.status === 'online' ? '在线' : '离线'}</span>
<span class="${statusClass}">${camera.stream_status === 'online' ? '在线' : '离线'}</span>
</p>
<small class="text-muted">
创建时间: ${new Date(camera.created_at).toLocaleString()}
创建时间: ${camera.create_time ? new Date(camera.create_time).toLocaleString() : '未知'}
</small>
</div>
<div class="card-footer">
<div class="d-flex btn-group-actions">
<button class="btn btn-sm btn-primary" onclick="editCamera('${camera.id}')">
<button class="btn btn-sm btn-primary" onclick="editCamera('${camera.camera_id || camera.id}')">
<i class="fas fa-edit"></i> 编辑
</button>
<button class="btn btn-sm btn-success" onclick="previewCamera('${camera.id}')">
<button class="btn btn-sm btn-success" onclick="previewCamera('${camera.camera_id || camera.id}')">
<i class="fas fa-play"></i> 预览
</button>
<button class="btn btn-sm btn-danger" onclick="deleteCamera('${camera.id}', '${camera.name}')">
<button class="btn btn-sm btn-danger" onclick="deleteCamera('${camera.camera_id || camera.id}', '${camera.camera_name || camera.name || 'undefined'}')">
<i class="fas fa-trash"></i> 删除
</button>
</div>
@ -268,13 +400,34 @@
// 添加摄像头
function addCamera() {
const cameraName = document.getElementById('addCameraName').value;
const unitCode = document.getElementById('addUnitCode').value;
const ip = document.getElementById('addIP').value;
const port = parseInt(document.getElementById('addPort').value) || 554;
const username = document.getElementById('addUsername').value;
const password = document.getElementById('addPassword').value;
const rtspUrl = document.getElementById('addRTSPURL').value;
const deviceType = document.getElementById('addDeviceType').value;
const cameraProduce = document.getElementById('addCameraProduce').value;
const nvrProduce = document.getElementById('addNvrProduce').value;
const nvrPath = document.getElementById('addNvrPath').value;
const playBack = document.getElementById('addPlayBack').value;
const enabled = document.getElementById('addEnabled').checked;
const formData = {
name: document.getElementById('addName').value,
ip: document.getElementById('addIP').value,
username: document.getElementById('addUsername').value,
password: document.getElementById('addPassword').value,
rtsp_url: document.getElementById('addRTSPURL').value,
enabled: document.getElementById('addEnabled').checked
camera_name: cameraName,
unit_code: unitCode,
ip: ip,
port: port,
username: username,
password: password,
url: rtspUrl,
device_type: deviceType,
camera_produce: cameraProduce,
nvr_produce: nvrProduce,
nvr_path: nvrPath,
play_back: playBack,
enabled: enabled
};
fetch('/camera/add', {
@ -306,12 +459,19 @@
fetch(`/camera/${cameraId}`)
.then(response => response.json())
.then(camera => {
document.getElementById('editCameraId').value = camera.id;
document.getElementById('editName').value = camera.name;
document.getElementById('editIP').value = camera.ip;
document.getElementById('editCameraId').value = camera.camera_id || camera.id;
document.getElementById('editCameraName').value = camera.camera_name || camera.name || '';
document.getElementById('editUnitCode').value = camera.unit_code || '';
document.getElementById('editIP').value = camera.ip || '';
document.getElementById('editPort').value = camera.port || 554;
document.getElementById('editUsername').value = camera.username || '';
document.getElementById('editPassword').value = '';
document.getElementById('editRTSPURL').value = camera.rtsp_url;
document.getElementById('editRTSPURL').value = camera.url || camera.rtsp_url || '';
document.getElementById('editDeviceType').value = camera.device_type || '';
document.getElementById('editCameraProduce').value = camera.camera_produce || '';
document.getElementById('editNvrProduce').value = camera.nvr_produce || '';
document.getElementById('editNvrPath').value = camera.nvr_path || '';
document.getElementById('editPlayBack').value = camera.play_back || '';
document.getElementById('editEnabled').checked = camera.enabled;
new bootstrap.Modal(document.getElementById('editCameraModal')).show();
@ -325,13 +485,34 @@
// 更新摄像头
function updateCamera() {
const cameraId = document.getElementById('editCameraId').value;
const cameraName = document.getElementById('editCameraName').value;
const unitCode = document.getElementById('editUnitCode').value;
const ip = document.getElementById('editIP').value;
const port = parseInt(document.getElementById('editPort').value) || 554;
const username = document.getElementById('editUsername').value;
const password = document.getElementById('editPassword').value;
const rtspUrl = document.getElementById('editRTSPURL').value;
const deviceType = document.getElementById('editDeviceType').value;
const cameraProduce = document.getElementById('editCameraProduce').value;
const nvrProduce = document.getElementById('editNvrProduce').value;
const nvrPath = document.getElementById('editNvrPath').value;
const playBack = document.getElementById('editPlayBack').value;
const enabled = document.getElementById('editEnabled').checked;
const formData = {
name: document.getElementById('editName').value,
ip: document.getElementById('editIP').value,
username: document.getElementById('editUsername').value,
password: document.getElementById('editPassword').value,
rtsp_url: document.getElementById('editRTSPURL').value,
enabled: document.getElementById('editEnabled').checked
camera_name: cameraName,
unit_code: unitCode,
ip: ip,
port: port,
username: username,
password: password,
url: rtspUrl,
device_type: deviceType,
camera_produce: cameraProduce,
nvr_produce: nvrProduce,
nvr_path: nvrPath,
play_back: playBack,
enabled: enabled
};
fetch(`/camera/${cameraId}`, {

Loading…
Cancel
Save