mirror of
https://gitee.com/NexIoT/Universal-IoT-Java.git
synced 2026-10-31 00:41:27 +08:00
refactor(wvp): wvp有安全风险,暂不开源
This commit is contained in:
@@ -1,52 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>cn-universal-protocol</artifactId>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>cn-wvp-protocol</artifactId>
|
||||
<name>cn-wvp-protocol</name>
|
||||
<description>WVP 视频平台协议下发转换模块</description>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-persistence</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-dm</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
-765
@@ -1,765 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2025, NexIoT. All Rights Reserved.
|
||||
*
|
||||
* @Description: WVP平台适配器实现
|
||||
* @Author: gitee.com/NexIoT
|
||||
*
|
||||
*/
|
||||
package cn.wvp.protocol.adapter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.universal.common.domain.R;
|
||||
import cn.universal.dm.device.service.action.IoTDeviceActionAfterService;
|
||||
import cn.universal.dm.video.VideoPlatformAdapter;
|
||||
import cn.universal.dm.video.VideoPlatformInstanceAdapter;
|
||||
import cn.universal.persistence.entity.VideoPlatformInstance;
|
||||
import cn.universal.persistence.mapper.VideoPlatformInstanceMapper;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
import cn.wvp.protocol.service.WvpDeviceService;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/** WVP平台适配器实现 将WvpDeviceService的能力适配到VideoPlatformAdapter统一接口 */
|
||||
@Slf4j
|
||||
@Component("wvpPlatformAdapter")
|
||||
@RequiredArgsConstructor
|
||||
public class WvpPlatformAdapter implements VideoPlatformAdapter, VideoPlatformInstanceAdapter {
|
||||
|
||||
private final WvpDeviceService wvpDeviceService;
|
||||
|
||||
private final IoTDeviceActionAfterService ioTDeviceActionAfterService;
|
||||
|
||||
private final VideoPlatformInstanceMapper videoPlatformInstanceMapper;
|
||||
|
||||
@Override
|
||||
public String getSupportedPlatformType() {
|
||||
return "wvp";
|
||||
}
|
||||
|
||||
/** 构建通用filters参数 */
|
||||
private Map<String, Object> buildFilters(VideoPlatformInstance instance) {
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
filters.put("endpoint", instance.getEndpoint());
|
||||
|
||||
// 从auth字段中提取token
|
||||
if (instance.getAuth() != null && !instance.getAuth().isEmpty()) {
|
||||
try {
|
||||
JSONObject auth = new JSONObject(instance.getAuth());
|
||||
if (auth.containsKey("token")) {
|
||||
filters.put("token", auth.getStr("token"));
|
||||
} else if (auth.containsKey("apiKey")) {
|
||||
filters.put("token", auth.getStr("apiKey"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析WVP auth配置失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return filters;
|
||||
}
|
||||
|
||||
/** 构建WvpDownRequest */
|
||||
private WvpDownRequest buildDownRequest(
|
||||
VideoPlatformInstance instance, String deviceId, JSONObject data) {
|
||||
WvpDownRequest request = new WvpDownRequest();
|
||||
request.setDeviceId(deviceId);
|
||||
request.setData(data);
|
||||
request.setWvpRequestData(buildFilters(instance));
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> testConnection(VideoPlatformInstance instance) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
Map<String, Object> filters = buildFilters(instance);
|
||||
filters.put("page", 1);
|
||||
filters.put("count", 1);
|
||||
|
||||
R<?> response = wvpDeviceService.listDevices(instance.getInstanceKey(), filters);
|
||||
long latency = System.currentTimeMillis() - start;
|
||||
|
||||
if (response.isSuccess()) {
|
||||
result.put("reachable", true);
|
||||
result.put("latencyMs", latency);
|
||||
result.put("message", "WVP连接正常");
|
||||
} else {
|
||||
result.put("reachable", false);
|
||||
result.put("latencyMs", latency);
|
||||
result.put("message", "WVP响应异常:" + response.getMsg());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
long latency = System.currentTimeMillis() - start;
|
||||
log.error("WVP连接测试失败:{}", e.getMessage());
|
||||
result.put("reachable", false);
|
||||
result.put("latencyMs", latency);
|
||||
result.put("message", "连接失败:" + e.getMessage());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> listDevices(
|
||||
VideoPlatformInstance instance, Map<String, Object> filters) {
|
||||
List<Map<String, Object>> devices = new ArrayList<>();
|
||||
|
||||
try {
|
||||
Map<String, Object> mergedFilters = buildFilters(instance);
|
||||
if (filters != null) {
|
||||
mergedFilters.putAll(filters);
|
||||
}
|
||||
|
||||
// 如果没有指定分页参数,使用默认值
|
||||
if (!mergedFilters.containsKey("page")) {
|
||||
mergedFilters.put("page", 1);
|
||||
}
|
||||
if (!mergedFilters.containsKey("count")) {
|
||||
mergedFilters.put("count", 100);
|
||||
}
|
||||
|
||||
R<?> response = wvpDeviceService.listDevices(instance.getInstanceKey(), mergedFilters);
|
||||
|
||||
if (response.isSuccess() && response.getData() != null) {
|
||||
|
||||
// 转换响应数据格式
|
||||
Object data = response.getData();
|
||||
if (data instanceof Map) {
|
||||
JSONObject dataJson = new JSONObject(data);
|
||||
if (dataJson.containsKey("list")) {
|
||||
JSONArray list = dataJson.getJSONArray("list");
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
JSONObject item = list.getJSONObject(i);
|
||||
Map<String, Object> device = new HashMap<>();
|
||||
// 主表公共字段
|
||||
String deviceId = item.getStr("deviceId");
|
||||
device.put("deviceId", deviceId);
|
||||
device.put("name", item.getStr("name", ""));
|
||||
device.put("manufacturer", item.getStr("manufacturer", ""));
|
||||
device.put("model", item.getStr("model", ""));
|
||||
// 根据onLine字段设置状态
|
||||
Boolean onLine = item.getBool("onLine", false);
|
||||
String status = onLine ? "online" : "offline";
|
||||
device.put("status", status);
|
||||
device.put("channelCount", item.getInt("channelCount", 0));
|
||||
device.put("ip", item.getStr("ip", ""));
|
||||
device.put("port", item.getInt("port", 0));
|
||||
// WVP扩展表特有字段
|
||||
device.put("charset", item.getStr("charset", ""));
|
||||
device.put("transport", item.getStr("transport", ""));
|
||||
device.put("streamMode", item.getStr("streamMode", ""));
|
||||
device.put("hostAddress", item.getStr("hostAddress", ""));
|
||||
device.put("registerTime", item.getStr("registerTime", ""));
|
||||
device.put("keepaliveTime", item.getStr("keepaliveTime", ""));
|
||||
device.put("expires", item.getInt("expires", 3600));
|
||||
device.put("mediaServerId", item.getStr("mediaServerId", ""));
|
||||
|
||||
// 只在forceSync时查询通道列表(用于同步写入数据库)
|
||||
// 常规查询应该从video_platform_channel表读取
|
||||
boolean isForceSync =
|
||||
mergedFilters.containsKey("forceSync")
|
||||
&& "true".equalsIgnoreCase(String.valueOf(mergedFilters.get("forceSync")));
|
||||
if (isForceSync) {
|
||||
List<Map<String, Object>> channelList = getDeviceChannels(instance, deviceId);
|
||||
device.put("channelList", channelList);
|
||||
}
|
||||
|
||||
devices.add(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warn("WVP设备列表查询失败:{}", response.getMsg());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("WVP设备列表查询异常:{}", e.getMessage(), e);
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getStreamUrl(
|
||||
VideoPlatformInstance instance, String deviceId, String channelId, String streamType) {
|
||||
Map<String, String> urls = new HashMap<>();
|
||||
|
||||
try {
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("channelId", channelId != null ? channelId : deviceId);
|
||||
data.set("protocol", "flv"); // 默认FLV
|
||||
|
||||
WvpDownRequest request = buildDownRequest(instance, deviceId, data);
|
||||
R<?> response = wvpDeviceService.startPreview(request);
|
||||
|
||||
log.info("WVP预览响应: success={}, data={}", response.isSuccess(), response.getData());
|
||||
|
||||
if (response.isSuccess() && response.getData() != null) {
|
||||
JSONObject result = new JSONObject(response.getData());
|
||||
log.info("WVP预览结果: {}", result.toString());
|
||||
|
||||
if (result.containsKey("urls")) {
|
||||
JSONObject urlsObj = result.getJSONObject("urls");
|
||||
// 遍历所有返回的流地址
|
||||
for (String key : urlsObj.keySet()) {
|
||||
Object value = urlsObj.get(key);
|
||||
// 过滤掉null值
|
||||
if (value != null && !"null".equals(String.valueOf(value))) {
|
||||
urls.put(key, String.valueOf(value));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 如果没有urls字段,直接使用根对象的所有流地址字段
|
||||
// 常见的流地址字段
|
||||
String[] streamKeys = {
|
||||
"flv",
|
||||
"https_flv",
|
||||
"ws_flv",
|
||||
"wss_flv",
|
||||
"fmp4",
|
||||
"https_fmp4",
|
||||
"ws_fmp4",
|
||||
"wss_fmp4",
|
||||
"hls",
|
||||
"https_hls",
|
||||
"ws_hls",
|
||||
"wss_hls",
|
||||
"ts",
|
||||
"https_ts",
|
||||
"ws_ts",
|
||||
"wss_ts",
|
||||
"rtmp",
|
||||
"rtmps",
|
||||
"rtsp",
|
||||
"rtsps",
|
||||
"rtc",
|
||||
"rtcs",
|
||||
"webrtc"
|
||||
};
|
||||
|
||||
for (String key : streamKeys) {
|
||||
if (result.containsKey(key)) {
|
||||
Object value = result.get(key);
|
||||
// 过滤掉null值
|
||||
if (value != null && !"null".equals(String.valueOf(value))) {
|
||||
urls.put(key, String.valueOf(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("WVP解析后的URLs: {}", urls);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("WVP播放地址获取异常:{}", e.getMessage(), e);
|
||||
}
|
||||
ioTDeviceActionAfterService.online(instance.getInstanceKey(), deviceId);
|
||||
return urls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void controlPTZ(
|
||||
VideoPlatformInstance instance,
|
||||
String extDeviceId,
|
||||
String channelId,
|
||||
String command,
|
||||
int speed) {
|
||||
try {
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("channelId", channelId);
|
||||
data.set("command", command);
|
||||
data.set("speed", speed);
|
||||
|
||||
WvpDownRequest request = buildDownRequest(instance, extDeviceId, data);
|
||||
R<?> response = wvpDeviceService.ptzControl(request);
|
||||
|
||||
if (!response.isSuccess()) {
|
||||
log.error("WVP PTZ控制失败:{}", response.getMsg());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("WVP PTZ控制异常:{}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> queryRecords(
|
||||
VideoPlatformInstance instance,
|
||||
String extDeviceId,
|
||||
String channelId,
|
||||
long startTime,
|
||||
long endTime) {
|
||||
List<Map<String, Object>> records = new ArrayList<>();
|
||||
|
||||
try {
|
||||
// WVP要求时间格式为 yyyy-MM-dd HH:mm:ss
|
||||
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
String reqStartTime = sdf.format(new java.util.Date(startTime));
|
||||
String reqEndTime = sdf.format(new java.util.Date(endTime));
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("channelId", channelId);
|
||||
data.set("startTime", reqStartTime);
|
||||
data.set("endTime", reqEndTime);
|
||||
|
||||
WvpDownRequest request = buildDownRequest(instance, extDeviceId, data);
|
||||
R<?> response = wvpDeviceService.queryGBRecords(request);
|
||||
|
||||
if (response.isSuccess() && response.getData() != null) {
|
||||
// 转换响应数据格式
|
||||
Object dataObj = response.getData();
|
||||
if (dataObj instanceof Map) {
|
||||
JSONObject dataJson = new JSONObject(dataObj);
|
||||
if (dataJson.containsKey("recordList")) {
|
||||
// WVP返回的录像列表在recordList字段中
|
||||
JSONArray list = dataJson.getJSONArray("recordList");
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
JSONObject item = list.getJSONObject(i);
|
||||
Map<String, Object> record = new HashMap<>();
|
||||
|
||||
// 生成唯一ID
|
||||
record.put("id", i + 1);
|
||||
|
||||
// 将WVP的时间字符串转为时间戳(毫秒)
|
||||
try {
|
||||
String respStartTime = item.getStr("startTime", "");
|
||||
String respEndTime = item.getStr("endTime", "");
|
||||
if (!respStartTime.isEmpty()) {
|
||||
record.put("startTime", sdf.parse(respStartTime).getTime());
|
||||
}
|
||||
if (!respEndTime.isEmpty()) {
|
||||
record.put("endTime", sdf.parse(respEndTime).getTime());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("录像时间解析失败: {}", e.getMessage());
|
||||
}
|
||||
|
||||
// 文件大小转换为可读格式
|
||||
long fileSize = item.getLong("fileSize", 0L);
|
||||
record.put("fileSize", formatFileSize(fileSize));
|
||||
record.put("fileSizeBytes", fileSize);
|
||||
|
||||
// 其他字段
|
||||
record.put("name", item.getStr("name", ""));
|
||||
record.put("filePath", item.getStr("filePath", ""));
|
||||
record.put("address", item.getStr("address", ""));
|
||||
record.put("secrecy", item.getInt("secrecy", 0));
|
||||
record.put("type", item.getStr("type", "time"));
|
||||
|
||||
records.add(record);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("WVP录像查询异常:{}", e.getMessage(), e);
|
||||
}
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map<String, Object>> listOrganizations(
|
||||
VideoPlatformInstance instance, Map<String, Object> filters) {
|
||||
// WVP无组织体系,返回空列表
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribe(VideoPlatformInstance instance, List<String> topics, String callback) {
|
||||
// WVP订阅暂不实现,通常通过Webhook配置
|
||||
log.info(
|
||||
"WVP订阅:instanceKey={}, topics={}, callback={}",
|
||||
instance.getInstanceKey(),
|
||||
topics,
|
||||
callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribe(VideoPlatformInstance instance, List<String> topics) {
|
||||
log.info("WVP取消订阅:instanceKey={}, topics={}", instance.getInstanceKey(), topics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getPlaybackUrl(
|
||||
VideoPlatformInstance instance,
|
||||
String deviceId,
|
||||
String channelId,
|
||||
long startTime,
|
||||
long endTime) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
|
||||
try {
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("channelId", channelId != null ? channelId : deviceId);
|
||||
data.set("startTime", startTime);
|
||||
data.set("endTime", endTime);
|
||||
|
||||
WvpDownRequest request = buildDownRequest(instance, deviceId, data);
|
||||
R<?> response = wvpDeviceService.startPlayback(request);
|
||||
|
||||
log.info("WVP回放响应: success={}, data={}", response.isSuccess(), response.getData());
|
||||
|
||||
if (response.isSuccess() && response.getData() != null) {
|
||||
// 解析响应数据
|
||||
Object responseData = response.getData();
|
||||
if (responseData instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> dataMap = (Map<String, Object>) responseData;
|
||||
|
||||
// 直接使用返回的所有字段(包括流地址和其他字段)
|
||||
result.putAll(dataMap);
|
||||
|
||||
log.info("WVP解析后的回放数据: {}", result);
|
||||
} else {
|
||||
log.warn("WVP回放响应数据格式异常: {}", responseData);
|
||||
}
|
||||
} else {
|
||||
log.error("WVP回放失败: {}", response.getMsg());
|
||||
throw new RuntimeException("WVP回放失败: " + response.getMsg());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("WVP回放地址获取异常:{}", e.getMessage(), e);
|
||||
throw new RuntimeException("获取回放地址失败: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 获取设备的通道列表 */
|
||||
private List<Map<String, Object>> getDeviceChannels(
|
||||
VideoPlatformInstance instance, String deviceId) {
|
||||
List<Map<String, Object>> channels = new ArrayList<>();
|
||||
|
||||
try {
|
||||
Map<String, Object> channelFilters = buildFilters(instance);
|
||||
channelFilters.put("page", 1);
|
||||
channelFilters.put("count", 100); // 通道一般不会太多
|
||||
|
||||
R<?> response = wvpDeviceService.listChannels(deviceId, channelFilters);
|
||||
|
||||
if (response.isSuccess() && response.getData() != null) {
|
||||
Object data = response.getData();
|
||||
if (data instanceof Map) {
|
||||
JSONObject dataJson = new JSONObject(data);
|
||||
if (dataJson.containsKey("list")) {
|
||||
JSONArray list = dataJson.getJSONArray("list");
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
JSONObject item = list.getJSONObject(i);
|
||||
Map<String, Object> channel = new HashMap<>();
|
||||
|
||||
// 通道公共字段
|
||||
channel.put("channelId", item.getStr("deviceId")); // WVP中通道的deviceId就是通道ID
|
||||
channel.put("name", item.getStr("name", ""));
|
||||
channel.put("manufacturer", item.getStr("manufacturer", ""));
|
||||
channel.put("model", item.getStr("model", ""));
|
||||
channel.put("owner", item.getStr("owner", ""));
|
||||
channel.put("civilCode", item.getStr("civilCode", ""));
|
||||
channel.put("address", item.getStr("address", ""));
|
||||
channel.put("parental", item.getInt("parental", 0));
|
||||
channel.put("parentId", item.getStr("parentId", ""));
|
||||
channel.put("safetyWay", item.getInt("safetyWay"));
|
||||
channel.put("registerWay", item.getInt("registerWay"));
|
||||
channel.put("secrecy", item.getInt("secrecy", 0));
|
||||
channel.put("ipAddress", item.getStr("ipAddress", ""));
|
||||
channel.put("port", item.getInt("port"));
|
||||
channel.put("status", item.getStr("status", "OFF"));
|
||||
channel.put("longitude", item.getStr("longitude"));
|
||||
channel.put("latitude", item.getStr("latitude"));
|
||||
channel.put("ptzType", item.getInt("ptzType", 0));
|
||||
channel.put("positionType", item.getInt("positionType"));
|
||||
|
||||
// WVP通道级特有字段
|
||||
channel.put("streamId", item.getStr("streamId", ""));
|
||||
channel.put("hasAudio", item.getBool("hasAudio", false));
|
||||
channel.put("subCount", item.getInt("subCount", 0));
|
||||
channel.put("streamIdentification", item.getStr("streamIdentification", ""));
|
||||
channel.put("channelType", item.getInt("channelType", 0));
|
||||
|
||||
channels.add(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("WVP通道列表查询异常:deviceId={}, error={}", deviceId, e.getMessage());
|
||||
}
|
||||
|
||||
return channels;
|
||||
}
|
||||
|
||||
/** 格式化文件大小 */
|
||||
private String formatFileSize(long bytes) {
|
||||
if (bytes < 1024) {
|
||||
return bytes + " B";
|
||||
} else if (bytes < 1024 * 1024) {
|
||||
return String.format("%.2f KB", bytes / 1024.0);
|
||||
} else if (bytes < 1024 * 1024 * 1024) {
|
||||
return String.format("%.2f MB", bytes / (1024.0 * 1024));
|
||||
} else {
|
||||
return String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024));
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 国标录像下载公开方法 ====================
|
||||
|
||||
/** 开始国标录像下载 */
|
||||
public R<Map<String, Object>> startGBRecordDownload(
|
||||
VideoPlatformInstance instance, String deviceId, String channelId, Map<String, Object> data) {
|
||||
try {
|
||||
JSONObject requestData = new JSONObject();
|
||||
requestData.set("channelId", channelId);
|
||||
|
||||
// 从 data 中获取参数
|
||||
if (data.containsKey("startTime")) {
|
||||
requestData.set("startTime", String.valueOf(data.get("startTime")));
|
||||
}
|
||||
if (data.containsKey("endTime")) {
|
||||
requestData.set("endTime", String.valueOf(data.get("endTime")));
|
||||
}
|
||||
if (data.containsKey("downloadSpeed")) {
|
||||
requestData.set("downloadSpeed", data.get("downloadSpeed"));
|
||||
}
|
||||
|
||||
WvpDownRequest request = buildDownRequest(instance, deviceId, requestData);
|
||||
R<?> response = wvpDeviceService.startGBRecordDownload(request);
|
||||
|
||||
if (response.isSuccess() && response.getData() != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> result = (Map<String, Object>) response.getData();
|
||||
return R.ok(result);
|
||||
}
|
||||
return R.error(response.getMsg(), null);
|
||||
} catch (Exception e) {
|
||||
log.error("WVP国标录像下载失败:{}", e.getMessage(), e);
|
||||
return R.error("下载失败:" + e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
/** 停止国标录像下载 */
|
||||
public R<Void> stopGBRecordDownload(
|
||||
VideoPlatformInstance instance, String deviceId, String channelId, Map<String, Object> data) {
|
||||
try {
|
||||
JSONObject requestData = new JSONObject();
|
||||
requestData.set("channelId", channelId);
|
||||
if (data.containsKey("stream")) {
|
||||
requestData.set("stream", String.valueOf(data.get("stream")));
|
||||
}
|
||||
|
||||
WvpDownRequest request = buildDownRequest(instance, deviceId, requestData);
|
||||
R<?> response = wvpDeviceService.stopGBRecordDownload(request);
|
||||
|
||||
if (response.isSuccess()) {
|
||||
return R.ok();
|
||||
}
|
||||
return R.error(response.getMsg());
|
||||
} catch (Exception e) {
|
||||
log.error("WVP停止录像下载失败:{}", e.getMessage(), e);
|
||||
return R.error("停止下载失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取国标录像下载进度 */
|
||||
public R<Map<String, Object>> getGBRecordDownloadProgress(
|
||||
VideoPlatformInstance instance, String deviceId, String channelId, String stream) {
|
||||
try {
|
||||
JSONObject requestData = new JSONObject();
|
||||
requestData.set("channelId", channelId);
|
||||
requestData.set("stream", stream);
|
||||
|
||||
WvpDownRequest request = buildDownRequest(instance, deviceId, requestData);
|
||||
R<?> response = wvpDeviceService.getGBRecordDownloadProgress(request);
|
||||
|
||||
if (response.isSuccess() && response.getData() != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> result = (Map<String, Object>) response.getData();
|
||||
return R.ok(result);
|
||||
}
|
||||
return R.error(response.getMsg(), null);
|
||||
} catch (Exception e) {
|
||||
log.error("WVP获取下载进度失败:{}", e.getMessage(), e);
|
||||
return R.error("获取进度失败:" + e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 云端录像公开方法 ====================
|
||||
|
||||
/** 查询云端录像日期列表 */
|
||||
public R<List<String>> queryCloudRecordDates(
|
||||
VideoPlatformInstance instance,
|
||||
String deviceId,
|
||||
String channelId,
|
||||
Map<String, String> params) {
|
||||
try {
|
||||
JSONObject requestData = new JSONObject();
|
||||
requestData.set("app", params.getOrDefault("app", "rtp"));
|
||||
requestData.set("stream", deviceId + "_" + channelId);
|
||||
if (params.containsKey("year")) {
|
||||
requestData.set("year", Integer.parseInt(params.get("year")));
|
||||
}
|
||||
if (params.containsKey("month")) {
|
||||
requestData.set("month", Integer.parseInt(params.get("month")));
|
||||
}
|
||||
|
||||
WvpDownRequest request = buildDownRequest(instance, deviceId, requestData);
|
||||
R<?> response = wvpDeviceService.queryCloudRecordDates(request);
|
||||
|
||||
if (response.isSuccess() && response.getData() != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> dates = (List<String>) response.getData();
|
||||
return R.ok(dates);
|
||||
}
|
||||
return R.error(response.getMsg(), null);
|
||||
} catch (Exception e) {
|
||||
log.error("WVP云端录像日期查询失败:{}", e.getMessage(), e);
|
||||
return R.error("查询失败:" + e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询云端录像列表 */
|
||||
public R<Map<String, Object>> queryCloudRecords(
|
||||
VideoPlatformInstance instance,
|
||||
String deviceId,
|
||||
String channelId,
|
||||
Map<String, String> params) {
|
||||
try {
|
||||
JSONObject requestData = new JSONObject();
|
||||
requestData.set("app", params.getOrDefault("app", "rtp"));
|
||||
requestData.set("stream", deviceId + "_" + channelId);
|
||||
requestData.set("page", Integer.parseInt(params.getOrDefault("page", "1")));
|
||||
requestData.set("count", Integer.parseInt(params.getOrDefault("count", "20")));
|
||||
if (params.containsKey("startTime")) {
|
||||
requestData.set("startTime", params.get("startTime"));
|
||||
}
|
||||
if (params.containsKey("endTime")) {
|
||||
requestData.set("endTime", params.get("endTime"));
|
||||
}
|
||||
|
||||
WvpDownRequest request = buildDownRequest(instance, deviceId, requestData);
|
||||
R<?> response = wvpDeviceService.queryCloudRecords(request);
|
||||
|
||||
if (response.isSuccess() && response.getData() != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> result = (Map<String, Object>) response.getData();
|
||||
return R.ok(result);
|
||||
}
|
||||
return R.error(response.getMsg(), null);
|
||||
} catch (Exception e) {
|
||||
log.error("WVP云端录像查询失败:{}", e.getMessage(), e);
|
||||
return R.error("查询失败:" + e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
/** 加载云端录像文件 */
|
||||
public R<Map<String, Object>> loadCloudRecord(
|
||||
VideoPlatformInstance instance, String deviceId, String channelId, Map<String, Object> data) {
|
||||
try {
|
||||
JSONObject requestData = new JSONObject();
|
||||
requestData.set("app", data.getOrDefault("app", "rtp"));
|
||||
requestData.set("stream", deviceId + "_" + channelId);
|
||||
if (data.containsKey("cloudRecordId")) {
|
||||
requestData.set("cloudRecordId", data.get("cloudRecordId"));
|
||||
}
|
||||
|
||||
WvpDownRequest request = buildDownRequest(instance, deviceId, requestData);
|
||||
R<?> response = wvpDeviceService.loadCloudRecord(request);
|
||||
|
||||
if (response.isSuccess() && response.getData() != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> result = (Map<String, Object>) response.getData();
|
||||
return R.ok(result);
|
||||
}
|
||||
return R.error(response.getMsg(), null);
|
||||
} catch (Exception e) {
|
||||
log.error("WVP云端录像加载失败:{}", e.getMessage(), e);
|
||||
return R.error("加载失败:" + e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
/** 云端录像定位 */
|
||||
public R<Void> seekCloudRecord(
|
||||
VideoPlatformInstance instance, String deviceId, String channelId, Map<String, Object> data) {
|
||||
try {
|
||||
JSONObject requestData = new JSONObject();
|
||||
requestData.putAll(data);
|
||||
|
||||
WvpDownRequest request = buildDownRequest(instance, deviceId, requestData);
|
||||
R<?> response = wvpDeviceService.seekCloudRecord(request);
|
||||
|
||||
if (response.isSuccess()) {
|
||||
return R.ok();
|
||||
}
|
||||
return R.error(response.getMsg());
|
||||
} catch (Exception e) {
|
||||
log.error("WVP云端录像定位失败:{}", e.getMessage(), e);
|
||||
return R.error("定位失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/** 设置云端录像倍速 */
|
||||
public R<Void> setCloudRecordSpeed(
|
||||
VideoPlatformInstance instance, String deviceId, String channelId, Map<String, Object> data) {
|
||||
try {
|
||||
JSONObject requestData = new JSONObject();
|
||||
requestData.putAll(data);
|
||||
|
||||
WvpDownRequest request = buildDownRequest(instance, deviceId, requestData);
|
||||
R<?> response = wvpDeviceService.setCloudRecordSpeed(request);
|
||||
|
||||
if (response.isSuccess()) {
|
||||
return R.ok();
|
||||
}
|
||||
return R.error(response.getMsg());
|
||||
} catch (Exception e) {
|
||||
log.error("WVP云端录像倍速设置失败:{}", e.getMessage(), e);
|
||||
return R.error("设置倍速失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取云端录像下载地址 */
|
||||
public R<Map<String, Object>> getCloudRecordPlayPath(
|
||||
VideoPlatformInstance instance, Integer recordId) {
|
||||
try {
|
||||
JSONObject requestData = new JSONObject();
|
||||
requestData.set("recordId", recordId);
|
||||
|
||||
WvpDownRequest request = buildDownRequest(instance, null, requestData);
|
||||
R<?> response = wvpDeviceService.getCloudRecordPlayPath(request);
|
||||
|
||||
if (response.isSuccess() && response.getData() != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> result = (Map<String, Object>) response.getData();
|
||||
return R.ok(result);
|
||||
}
|
||||
return R.error(response.getMsg(), null);
|
||||
} catch (Exception e) {
|
||||
log.error("WVP获取云端录像下载地址失败:{}", e.getMessage(), e);
|
||||
return R.error("获取下载地址失败:" + e.getMessage(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(
|
||||
cacheNames = "getVideoPlatformInstance",
|
||||
key = "#p0",
|
||||
unless = "#result == null")
|
||||
public VideoPlatformInstance getVideoPlatformInstance(String instanceKey) {
|
||||
if (StrUtil.isBlank(instanceKey)) {
|
||||
log.warn("wvp query instanceKey is blank");
|
||||
return null;
|
||||
}
|
||||
VideoPlatformInstance instance = new VideoPlatformInstance();
|
||||
instance.setInstanceKey(instanceKey);
|
||||
instance = videoPlatformInstanceMapper.selectOne(instance);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2025, NexIoT. All Rights Reserved.
|
||||
*
|
||||
* @Description: 本文件由 gitee.com/NexIoT 开发并拥有版权,未经授权严禁擅自商用、复制或传播。
|
||||
* @Author: gitee.com/NexIoT
|
||||
* @Email: wo8335224@gmail.com
|
||||
* @Wechat: outlookFil
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package cn.wvp.protocol.config;
|
||||
|
||||
import cn.universal.core.protocol.ProtocolModuleRuntimeRegistry;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* WVP 协议模块自动配置类
|
||||
*
|
||||
* <p>当 wvp.protocol.enabled=true 时,自动扫描包并创建所有WVP相关的Bean
|
||||
*
|
||||
* <p>通过 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 实现 Spring Boot 自动配置
|
||||
*
|
||||
* @version 2.0
|
||||
* @Author gitee.com/NexIoT
|
||||
* @since 2025/1/8
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(name = "wvp.protocol.enabled", havingValue = "true", matchIfMissing = false)
|
||||
@ComponentScan(basePackages = "cn.wvp.protocol")
|
||||
@EnableConfigurationProperties(WvpProperties.class)
|
||||
@Slf4j
|
||||
public class WvpAutoConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private WvpModuleInfo moduleInfo;
|
||||
|
||||
public WvpAutoConfiguration() {
|
||||
log.info("[WVP协议] WVP协议模块自动配置已启用");
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void registerProtocol() {
|
||||
if (moduleInfo != null) {
|
||||
ProtocolModuleRuntimeRegistry.registerProtocol(moduleInfo);
|
||||
log.info("[WVP自动配置] 协议模块已注册到运行时注册表");
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void unregisterProtocol() {
|
||||
if (moduleInfo != null) {
|
||||
ProtocolModuleRuntimeRegistry.unregisterProtocol(moduleInfo.getCode());
|
||||
log.info("[WVP自动配置] 协议模块已从运行时注册表注销");
|
||||
}
|
||||
}
|
||||
}
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2025, NexIoT. All Rights Reserved.
|
||||
*
|
||||
* @Description: 本文件由 gitee.com/NexIoT 开发并拥有版权,未经授权严禁擅自商用、复制或传播。
|
||||
* @Author: gitee.com/NexIoT
|
||||
* @Email: wo8335224@gmail.com
|
||||
* @Wechat: outlookFil
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package cn.wvp.protocol.config;
|
||||
|
||||
import cn.universal.core.protocol.ProtocolModuleInfo;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* WVP协议模块信息
|
||||
*
|
||||
* @version 2.0
|
||||
* @Author gitee.com/NexIoT
|
||||
* @since 2025/1/8
|
||||
*/
|
||||
@Component
|
||||
public class WvpModuleInfo implements ProtocolModuleInfo {
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return "wvp";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "WVP国标视频平台";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return "2.0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "WVP(Web Video Platform)国标视频平台协议适配器";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProtocolCategory getCategory() {
|
||||
return ProtocolCategory.PLATFORM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isManualCreatable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNotCreatableReason() {
|
||||
return "视频平台产品采用懒创建策略,请在视频中心中勾选设备时自动创建";
|
||||
}
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2025, NexIoT. All Rights Reserved.
|
||||
*
|
||||
* @Description: 本文件由 gitee.com/NexIoT 开发并拥有版权,未经授权严禁擅自商用、复制或传播。
|
||||
* @Author: gitee.com/NexIoT
|
||||
* @Email: wo8335224@gmail.com
|
||||
* @Wechat: outlookFil
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package cn.wvp.protocol.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* WVP 协议配置属性
|
||||
*
|
||||
* @version 2.0
|
||||
* @Author gitee.com/NexIoT
|
||||
* @since 2025/1/8
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "wvp.protocol")
|
||||
@Data
|
||||
public class WvpProperties {
|
||||
|
||||
/** 是否启用WVP协议模块 */
|
||||
private boolean enabled = true;
|
||||
}
|
||||
-90
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025, NexIoT. All Rights Reserved.
|
||||
*/
|
||||
package cn.wvp.protocol.constant;
|
||||
|
||||
/**
|
||||
* WVP平台API路径常量
|
||||
* 基于wvp-GB28181-pro标准接口
|
||||
*/
|
||||
public interface WvpApiConstants {
|
||||
|
||||
/** 设备查询接口 */
|
||||
String API_DEVICE_QUERY = "/api/device/query/devices";
|
||||
|
||||
/** 通道查询接口 */
|
||||
String API_DEVICE_CHANNELS = "/api/device/query/devices/%s/channels";
|
||||
|
||||
/** 云台控制接口 (方向+缩放) */
|
||||
String API_PTZ_CONTROL = "/api/front-end/ptz/%s/%s";
|
||||
|
||||
/** 光圈控制接口 */
|
||||
String API_IRIS_CONTROL = "/api/front-end/fi/iris/%s/%s";
|
||||
|
||||
/** 聚焦控制接口 */
|
||||
String API_FOCUS_CONTROL = "/api/front-end/fi/focus/%s/%s";
|
||||
|
||||
/** 预置位查询接口 */
|
||||
String API_PRESET_QUERY = "/api/front-end/preset/query/%s/%s";
|
||||
|
||||
/** 抓拍接口 */
|
||||
String API_SNAPSHOT = "/api/play/snap";
|
||||
|
||||
// ==================== 国标录像相关接口 ====================
|
||||
|
||||
/** 国标录像查询接口 */
|
||||
String API_GB_RECORD_QUERY = "/api/gb_record/query/%s/%s";
|
||||
|
||||
/** 国标录像下载开始 */
|
||||
String API_GB_RECORD_DOWNLOAD_START = "/api/gb_record/download/start/%s/%s";
|
||||
|
||||
/** 国标录像下载停止 */
|
||||
String API_GB_RECORD_DOWNLOAD_STOP = "/api/gb_record/download/stop/%s/%s/%s";
|
||||
|
||||
/** 国标录像下载进度 */
|
||||
String API_GB_RECORD_DOWNLOAD_PROGRESS = "/api/gb_record/download/progress/%s/%s/%s";
|
||||
|
||||
// ==================== 云端录像相关接口 ====================
|
||||
|
||||
/** 云端录像日期列表 */
|
||||
String API_CLOUD_RECORD_DATE_LIST = "/api/cloud/record/date/list";
|
||||
|
||||
/** 云端录像列表查询 */
|
||||
String API_CLOUD_RECORD_LIST = "/api/cloud/record/list";
|
||||
|
||||
/** 加载云端录像文件 */
|
||||
String API_CLOUD_RECORD_LOAD = "/api/cloud/record/loadRecord";
|
||||
|
||||
/** 云端录像定位 */
|
||||
String API_CLOUD_RECORD_SEEK = "/api/cloud/record/seek";
|
||||
|
||||
/** 云端录像倍速 */
|
||||
String API_CLOUD_RECORD_SPEED = "/api/cloud/record/speed";
|
||||
|
||||
/** 云端录像删除 */
|
||||
String API_CLOUD_RECORD_DELETE = "/api/cloud/record/delete";
|
||||
|
||||
/** 云端录像收藏添加 */
|
||||
String API_CLOUD_RECORD_COLLECT_ADD = "/api/cloud/record/collect/add";
|
||||
|
||||
/** 云端录像收藏删除 */
|
||||
String API_CLOUD_RECORD_COLLECT_DELETE = "/api/cloud/record/collect/delete";
|
||||
|
||||
/** 直播开始接口 */
|
||||
String API_PLAY_START = "/api/play/start/%s/%s";
|
||||
|
||||
/** 直播停止接口 */
|
||||
String API_PLAY_STOP = "/api/play/stop/%s/%s";
|
||||
|
||||
/** 回放开始接口 */
|
||||
String API_PLAYBACK_START = "/api/playback/start/%s/%s";
|
||||
|
||||
/** 回放停止接口 */
|
||||
String API_PLAYBACK_STOP = "/api/playback/stop/%s/%s";
|
||||
|
||||
/** 云端录像下载地址获取 */
|
||||
String API_CLOUD_RECORD_PLAY_PATH = "/api/cloud/record/play/path";
|
||||
|
||||
/** Token请求头名称 */
|
||||
String HEADER_ACCESS_TOKEN = "Access-Token";
|
||||
}
|
||||
-194
@@ -1,194 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2025, NexIoT. All Rights Reserved.
|
||||
*
|
||||
* @Description: 本文件由 gitee.com/NexIoT 开发并拥有版权,未经授权严禁擅自商用、复制或传播。
|
||||
* @Author: gitee.com/NexIoT
|
||||
* @Email: wo8335224@gmail.com
|
||||
* @Wechat: outlookFil
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package cn.wvp.protocol.converter;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.universal.common.constant.IoTConstant.DownCmd;
|
||||
import cn.universal.core.downlink.DownlinkContext;
|
||||
import cn.universal.core.downlink.converter.DownRequestConverter;
|
||||
import cn.universal.core.message.UnifiedDownlinkCommand;
|
||||
import cn.universal.dm.device.service.AbstratIoTService;
|
||||
import cn.universal.dm.video.VideoPlatformInstanceAdapter;
|
||||
import cn.universal.persistence.dto.IoTDeviceDTO;
|
||||
import cn.universal.persistence.entity.IoTProduct;
|
||||
import cn.universal.persistence.entity.VideoPlatformInstance;
|
||||
import cn.universal.persistence.query.IoTDeviceQuery;
|
||||
import cn.wvp.protocol.config.WvpModuleInfo;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* WVP协议转换器 将UnifiedDownlinkCommand转换为WvpDownRequest
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025/11/09
|
||||
*/
|
||||
@Slf4j
|
||||
@Component("wvpConverter")
|
||||
public class WvpDownRequestConverter extends AbstratIoTService
|
||||
implements DownRequestConverter<WvpDownRequest> {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("wvpPlatformAdapter")
|
||||
private VideoPlatformInstanceAdapter videoPlatformInstanceAdapter;
|
||||
|
||||
@Resource private WvpModuleInfo wvpModuleInfo;
|
||||
|
||||
@Override
|
||||
public WvpDownRequest convert(UnifiedDownlinkCommand command, DownlinkContext<?> context) {
|
||||
try {
|
||||
// 1. 加载产品信息
|
||||
IoTProduct ioTProduct = getProduct(command.getProductKey());
|
||||
if (ioTProduct == null) {
|
||||
throw new IllegalArgumentException("产品不存在: " + command.getProductKey());
|
||||
}
|
||||
|
||||
// 2. 创建WVP下行请求
|
||||
WvpDownRequest request = new WvpDownRequest();
|
||||
BeanUtil.copyProperties(command, request, "extensions", "metadata");
|
||||
|
||||
// 3. 设置产品信息
|
||||
request.setIoTProduct(ioTProduct);
|
||||
|
||||
// 4. 加载设备信息(对于DEV_ADD命令,设备可能不存在)
|
||||
IoTDeviceDTO ioTDeviceDTO = null;
|
||||
if (command.getCmd() != DownCmd.DEV_ADD && command.getCmd() != DownCmd.DEV_ADDS) {
|
||||
ioTDeviceDTO =
|
||||
getIoTDeviceDTO(
|
||||
IoTDeviceQuery.builder()
|
||||
.productKey(command.getProductKey())
|
||||
.deviceId(command.getDeviceId())
|
||||
.iotId(command.getIotId())
|
||||
.build());
|
||||
|
||||
if (ioTDeviceDTO == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"设备不存在: productKey="
|
||||
+ command.getProductKey()
|
||||
+ ", deviceId="
|
||||
+ command.getDeviceId());
|
||||
}
|
||||
}
|
||||
request.setIoTDeviceDTO(ioTDeviceDTO);
|
||||
|
||||
VideoPlatformInstance videoPlatformInstance =
|
||||
videoPlatformInstanceAdapter.getVideoPlatformInstance(command.getProductKey());
|
||||
if (videoPlatformInstance == null) {
|
||||
throw new IllegalArgumentException("视频实例不存在: " + command.getProductKey());
|
||||
}
|
||||
buildEndpointAuth(request, videoPlatformInstance);
|
||||
// 5. 处理扩展字段
|
||||
if (command.getExtensions() != null && !command.getExtensions().isEmpty()) {
|
||||
Map<String, Object> wvpRequestData = request.getWvpRequestData();
|
||||
wvpRequestData.putAll(command.getExtensions());
|
||||
request.setWvpRequestData(wvpRequestData);
|
||||
}
|
||||
|
||||
// 6. 设置消息ID
|
||||
if (StrUtil.isBlank(request.getMsgId())) {
|
||||
request.setMsgId(generateMsgId());
|
||||
}
|
||||
if (command.getFunction() != null && !command.getFunction().isEmpty()) {
|
||||
request.setFunction(command.getFunction());
|
||||
if (command.getFunction().containsKey("data")) {
|
||||
request.setData(JSONUtil.parseObj(command.getFunction().get("data")));
|
||||
}
|
||||
}
|
||||
log.debug(
|
||||
"[WVP转换器] 转换完成: productKey={}, deviceId={}, cmd={}",
|
||||
request.getProductKey(),
|
||||
request.getDeviceId(),
|
||||
request.getCmd() != null ? request.getCmd().getValue() : null);
|
||||
|
||||
return request;
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.error("[WVP转换器] 参数验证失败: {}", e.getMessage());
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.error("[WVP转换器] 转换失败", e);
|
||||
throw new IllegalStateException("WVP请求转换失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/** 生成消息ID */
|
||||
private String generateMsgId() {
|
||||
return String.valueOf(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String supportedProtocol() {
|
||||
return wvpModuleInfo.getCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(UnifiedDownlinkCommand command) {
|
||||
return command.getCmd() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preConvert(UnifiedDownlinkCommand command, DownlinkContext<?> context) {
|
||||
if (StrUtil.isBlank(command.getProductKey())) {
|
||||
throw new IllegalArgumentException("productKey不能为空");
|
||||
}
|
||||
|
||||
log.debug(
|
||||
"[WVP转换器] 开始转换: productKey={}, deviceId={}, cmd={}",
|
||||
command.getProductKey(),
|
||||
command.getDeviceId(),
|
||||
command.getCmd());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postConvert(
|
||||
UnifiedDownlinkCommand command, WvpDownRequest request, DownlinkContext<?> context) {
|
||||
context.setAttribute("convertTime", System.currentTimeMillis());
|
||||
context.setAttribute("protocolType", "WVP");
|
||||
|
||||
if (request.getIoTDeviceDTO() != null) {
|
||||
context.setAttribute("deviceName", request.getIoTDeviceDTO().getDeviceName());
|
||||
context.setAttribute("deviceStatus", request.getIoTDeviceDTO().getState());
|
||||
}
|
||||
|
||||
log.debug("[WVP转换器] 转换后处理完成: msgId={}", request.getMsgId());
|
||||
}
|
||||
|
||||
private void buildEndpointAuth(WvpDownRequest request, VideoPlatformInstance instance) {
|
||||
Map<String, Object> wvpRequestData = request.getWvpRequestData();
|
||||
if (wvpRequestData == null) {
|
||||
wvpRequestData = new HashMap<>();
|
||||
}
|
||||
String auth = instance.getAuth();
|
||||
if (JSONUtil.isTypeJSON(auth)) {
|
||||
wvpRequestData.putAll(JSONUtil.toBean(auth, Map.class));
|
||||
}
|
||||
String endpoint = instance.getEndpoint();
|
||||
if (StrUtil.isNotBlank(endpoint)) {
|
||||
wvpRequestData.put("endpoint", endpoint);
|
||||
}
|
||||
request.setWvpRequestData(wvpRequestData);
|
||||
}
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
package cn.wvp.protocol.entity;
|
||||
|
||||
import cn.universal.common.constant.IoTConstant.DownCmd;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* WVP 下行请求封装
|
||||
*/
|
||||
public class WvpDownRequest extends cn.universal.persistence.base.BaseDownRequest {
|
||||
private DownCmd cmd;
|
||||
private String productKey;
|
||||
private String deviceId;
|
||||
private Map<String, Object> wvpRequestData;
|
||||
|
||||
public DownCmd getCmd() {
|
||||
return cmd;
|
||||
}
|
||||
|
||||
public void setCmd(DownCmd cmd) {
|
||||
this.cmd = cmd;
|
||||
}
|
||||
|
||||
public String getProductKey() {
|
||||
return productKey;
|
||||
}
|
||||
|
||||
public void setProductKey(String productKey) {
|
||||
this.productKey = productKey;
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setDeviceId(String deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
public Map<String, Object> getWvpRequestData() {
|
||||
return wvpRequestData;
|
||||
}
|
||||
|
||||
public void setWvpRequestData(Map<String, Object> wvpRequestData) {
|
||||
this.wvpRequestData = wvpRequestData;
|
||||
}
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
package cn.wvp.protocol.handle;
|
||||
|
||||
import cn.universal.common.constant.IoTConstant.DownCmd;
|
||||
import cn.universal.common.domain.R;
|
||||
import cn.universal.persistence.base.IoTDownAdapter;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
import cn.wvp.protocol.handler.WvpCommandHandlerManager;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.Objects;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* WVP 下行实际处理入口(统一命令分发模式)
|
||||
*/
|
||||
@Component
|
||||
public class WvpDownHandle extends IoTDownAdapter<WvpDownRequest> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(WvpDownHandle.class);
|
||||
|
||||
@Resource private WvpCommandHandlerManager commandHandlerManager;
|
||||
|
||||
public R<?> down(WvpDownRequest downRequest) {
|
||||
if (downRequest == null || downRequest.getCmd() == null) {
|
||||
log.warn("WVP设备处理下行对象为空,不处理={}", downRequest);
|
||||
return R.error("WVP设备处理下行对象为空");
|
||||
}
|
||||
|
||||
R<?> preResult =
|
||||
beforeDownAction(downRequest.getIoTProduct(), downRequest.getData(), downRequest);
|
||||
if (Objects.nonNull(preResult)) {
|
||||
return preResult;
|
||||
}
|
||||
|
||||
return dispatchCommand(downRequest);
|
||||
}
|
||||
|
||||
private R<?> dispatchCommand(WvpDownRequest downRequest) {
|
||||
DownCmd cmd = downRequest.getCmd();
|
||||
switch (cmd) {
|
||||
case DEV_ADD:
|
||||
case DEV_DEL:
|
||||
case DEV_UPDATE:
|
||||
return commandHandlerManager.handleCommand(downRequest);
|
||||
case DEV_FUNCTION:
|
||||
return commandHandlerManager.handleCommand(downRequest);
|
||||
default:
|
||||
log.info("WVP设备处理下行未匹配到方法: {}", cmd);
|
||||
return R.error("未支持的命令类型: " + cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
package cn.wvp.protocol.handler;
|
||||
|
||||
import cn.universal.common.domain.R;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
|
||||
public interface WvpCommandHandler {
|
||||
String getSupportedCommand();
|
||||
R<?> handle(WvpDownRequest request);
|
||||
}
|
||||
-108
@@ -1,108 +0,0 @@
|
||||
package cn.wvp.protocol.handler;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.universal.common.domain.R;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* WVP命令处理器管理器
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class WvpCommandHandlerManager implements ApplicationContextAware {
|
||||
|
||||
private Map<String, WvpCommandHandler> handlerMap = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
Map<String, WvpCommandHandler> current =
|
||||
applicationContext.getBeansOfType(WvpCommandHandler.class);
|
||||
if (current != null && MapUtil.isNotEmpty(current)) {
|
||||
current.forEach(
|
||||
(key, handler) -> {
|
||||
String command = handler.getSupportedCommand();
|
||||
if (command != null && !command.isEmpty()) {
|
||||
handlerMap.put(command.toLowerCase(), handler);
|
||||
log.info("注册WVP命令处理器: {} -> {}", command, handler.getClass().getSimpleName());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理命令
|
||||
*
|
||||
* @param request 下行请求
|
||||
* @return 处理结果
|
||||
*/
|
||||
public R<?> handleCommand(WvpDownRequest request) {
|
||||
String commandKey = getCommandKey(request);
|
||||
if (commandKey == null) {
|
||||
log.warn("WVP设备处理功能下行无法确定命令类型,不处理={}", request);
|
||||
return R.error("无法确定命令类型");
|
||||
}
|
||||
|
||||
WvpCommandHandler handler = handlerMap.get(commandKey);
|
||||
if (handler == null) {
|
||||
log.info("WVP设备处理功能下行未匹配到处理器: {}", commandKey);
|
||||
return R.error("未找到对应的处理器: " + commandKey);
|
||||
}
|
||||
|
||||
try {
|
||||
return handler.handle(request);
|
||||
} catch (Exception e) {
|
||||
log.error("处理命令时发生异常: {}", commandKey, e);
|
||||
return R.error("处理命令时发生异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取命令键值
|
||||
* 兼容两种命令格式:
|
||||
* 1. DEV_FUNCTION 类型:从 function.function 字段或 data.function 字段获取
|
||||
* 2. DEV_ADD/DEV_DEL/DEV_UPDATE 类型:从 cmd 字段获取
|
||||
*
|
||||
* @param request 下行请求
|
||||
* @return 命令键值
|
||||
*/
|
||||
private String getCommandKey(WvpDownRequest request) {
|
||||
// 首先尝试从 function.function 字段获取(DEV_FUNCTION 类型,新方式)
|
||||
if (request.getFunction() != null) {
|
||||
String function = (String) request.getFunction().get("function");
|
||||
if (function != null && !function.isEmpty()) {
|
||||
return function.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
// 其次尝试从 data.function 字段获取(DEV_FUNCTION 类型,兼容旧方式)
|
||||
if (request.getData() != null) {
|
||||
String function = request.getData().getStr("function");
|
||||
if (function != null && !function.isEmpty()) {
|
||||
return function.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
// 如果 function 为空,则从 cmd 字段获取(DEV_ADD/DEV_DEL/DEV_UPDATE 类型)
|
||||
if (request.getCmd() != null) {
|
||||
return request.getCmd().name().toLowerCase();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有支持的命令
|
||||
*
|
||||
* @return 支持的命令列表
|
||||
*/
|
||||
public String[] getSupportedCommands() {
|
||||
return handlerMap.keySet().toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
package cn.wvp.protocol.handler.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.universal.common.constant.IoTConstant.DeviceStatus;
|
||||
import cn.universal.common.constant.IoTConstant.DownCmd;
|
||||
import cn.universal.common.domain.R;
|
||||
import cn.universal.persistence.base.IoTDeviceLifeCycle;
|
||||
import cn.universal.persistence.dto.IoTDeviceDTO;
|
||||
import cn.universal.persistence.entity.IoTDevice;
|
||||
import cn.universal.persistence.mapper.IoTDeviceMapper;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
import cn.wvp.protocol.handler.WvpCommandHandler;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class WvpDeviceAddHandler implements WvpCommandHandler {
|
||||
|
||||
@Resource(name = "ioTDeviceActionAfterService")
|
||||
private IoTDeviceLifeCycle ioTDeviceLifeCycle;
|
||||
|
||||
@Resource private IoTDeviceMapper ioTDeviceMapper;
|
||||
|
||||
public R<?> handle(WvpDownRequest downRequest) {
|
||||
// 幂等检查:productKey + deviceId
|
||||
Map<String,Object> query = new HashMap<>();
|
||||
query.put("iotId", downRequest.getProductKey() + downRequest.getDeviceId());
|
||||
IoTDeviceDTO exist = ioTDeviceMapper.selectIoTDeviceBO(query);
|
||||
if (exist != null) {
|
||||
return R.error("设备已存在");
|
||||
}
|
||||
// TODO: 接入WVP级联/GB注册绑定逻辑
|
||||
IoTDevice ioTDevice = IoTDevice.builder()
|
||||
.deviceId(downRequest.getDeviceId())
|
||||
.createTime(System.currentTimeMillis() / 1000)
|
||||
.deviceName(StrUtil.emptyToDefault(downRequest.getDetail(), downRequest.getDeviceId()))
|
||||
.state(DeviceStatus.offline.getCode())
|
||||
.iotId(downRequest.getProductKey() + downRequest.getDeviceId())
|
||||
.creatorId(downRequest.getAppUnionId())
|
||||
.productName(downRequest.getIoTProduct().getName())
|
||||
.application(downRequest.getApplicationId())
|
||||
.detail(downRequest.getDetail())
|
||||
.productKey(downRequest.getProductKey())
|
||||
.build();
|
||||
Map<String, Object> config = new HashMap<>();
|
||||
config.put("discardValue", 0);
|
||||
ioTDevice.setConfiguration(JSONUtil.toJsonStr(config));
|
||||
ioTDeviceMapper.insertUseGeneratedKeys(ioTDevice);
|
||||
ioTDeviceLifeCycle.create(downRequest.getProductKey(), downRequest.getDeviceId(), downRequest);
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("iotId", ioTDevice.getIotId());
|
||||
if (StrUtil.isNotBlank(downRequest.getIoTProduct().getMetadata())) {
|
||||
result.put("metadata", JSONUtil.parseObj(downRequest.getIoTProduct().getMetadata()));
|
||||
}
|
||||
result.put("productKey", downRequest.getProductKey());
|
||||
result.put("deviceNode", downRequest.getIoTProduct().getDeviceNode());
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
public String getSupportedCommand() {
|
||||
return DownCmd.DEV_ADD.name().toLowerCase();
|
||||
}
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
package cn.wvp.protocol.handler.impl;
|
||||
|
||||
import cn.universal.common.constant.IoTConstant.DownCmd;
|
||||
import cn.universal.common.domain.R;
|
||||
import cn.universal.dm.device.service.impl.IoTDeviceService;
|
||||
import cn.universal.persistence.base.IoTDeviceLifeCycle;
|
||||
import cn.universal.persistence.dto.IoTDeviceDTO;
|
||||
import cn.universal.persistence.mapper.IoTDeviceMapper;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
import cn.wvp.protocol.handler.WvpCommandHandler;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class WvpDeviceDeleteHandler implements WvpCommandHandler {
|
||||
|
||||
@Resource(name = "ioTDeviceActionAfterService")
|
||||
private IoTDeviceLifeCycle ioTDeviceLifeCycle;
|
||||
@Resource private IoTDeviceMapper ioTDeviceMapper;
|
||||
@Resource private IoTDeviceService iotDeviceService;
|
||||
|
||||
public R<?> handle(WvpDownRequest downRequest) {
|
||||
// 幂等检查
|
||||
Map<String,Object> param = new HashMap<>();
|
||||
param.put("iotId", downRequest.getProductKey() + downRequest.getDeviceId());
|
||||
IoTDeviceDTO dto = ioTDeviceMapper.selectIoTDeviceBO(param);
|
||||
if (dto == null) {
|
||||
return R.error("设备不存在");
|
||||
}
|
||||
// TODO: 接入WVP解绑逻辑,若未绑定则直接删除本地
|
||||
ioTDeviceLifeCycle.delete(dto, downRequest);
|
||||
iotDeviceService.delDevInstance(dto.getIotId());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
public String getSupportedCommand() {
|
||||
return DownCmd.DEV_DEL.name().toLowerCase();
|
||||
}
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
package cn.wvp.protocol.handler.impl;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.universal.common.constant.IoTConstant.DownCmd;
|
||||
import cn.universal.common.domain.R;
|
||||
import cn.universal.persistence.entity.IoTDevice;
|
||||
import cn.universal.persistence.mapper.IoTDeviceMapper;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
import cn.wvp.protocol.handler.WvpCommandHandler;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.Map;
|
||||
import org.springframework.stereotype.Component;
|
||||
import tk.mybatis.mapper.entity.Example;
|
||||
|
||||
@Component
|
||||
public class WvpDeviceUpdateHandler implements WvpCommandHandler {
|
||||
@Resource private IoTDeviceMapper ioTDeviceMapper;
|
||||
|
||||
public R<?> handle(WvpDownRequest request) {
|
||||
Map<String, Object> data = request.getWvpRequestData();
|
||||
if (data == null || MapUtil.isEmpty(data)) return R.error("缺少参数: data");
|
||||
String newName = (String) data.get("deviceName");
|
||||
if (newName == null || newName.isEmpty()) return R.error("缺少参数: deviceName");
|
||||
String iotId = request.getProductKey() + request.getDeviceId();
|
||||
String latitude = (String) data.get("latitude");
|
||||
String longitude = (String) data.get("longitude");
|
||||
// 根据iotId更新设备名称
|
||||
IoTDevice dev = new IoTDevice();
|
||||
dev.setDeviceName(newName);
|
||||
if (StrUtil.isNotBlank(latitude) && StrUtil.isNotBlank(longitude)) {
|
||||
dev.setCoordinate(longitude + "," + latitude);
|
||||
}
|
||||
|
||||
// 使用Example构建更新条件,根据iotId更新
|
||||
Example example = new Example(IoTDevice.class);
|
||||
example.createCriteria().andEqualTo("iotId", iotId);
|
||||
int count = ioTDeviceMapper.updateByExampleSelective(dev, example);
|
||||
if (count > 0) {
|
||||
return R.ok("更新成功");
|
||||
}
|
||||
return R.ok("无变化");
|
||||
}
|
||||
|
||||
public String getSupportedCommand() {
|
||||
return DownCmd.DEV_UPDATE.name().toLowerCase();
|
||||
}
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
package cn.wvp.protocol.handler.impl;
|
||||
|
||||
import cn.universal.common.constant.CameraCommand;
|
||||
import cn.universal.common.domain.R;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
import cn.wvp.protocol.handler.WvpCommandHandler;
|
||||
import cn.wvp.protocol.service.WvpDeviceService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class WvpLiveStreamHandler implements WvpCommandHandler {
|
||||
@Resource private WvpDeviceService wvpDeviceService;
|
||||
|
||||
public R<?> handle(WvpDownRequest request) {
|
||||
String function = request.getData() == null ? null : request.getData().getStr("function");
|
||||
return wvpDeviceService.startPreview(request);
|
||||
}
|
||||
public String getSupportedCommand() { return CameraCommand.CAMERA_LIVE_STREAM.getFunctionName(); }
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
package cn.wvp.protocol.handler.impl;
|
||||
|
||||
import cn.universal.common.constant.CameraCommand;
|
||||
import cn.universal.common.domain.R;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
import cn.wvp.protocol.handler.WvpCommandHandler;
|
||||
import cn.wvp.protocol.service.WvpDeviceService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* WVP加载云端录像处理器
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025/11/09
|
||||
*/
|
||||
@Component
|
||||
public class WvpLoadCloudRecordHandler implements WvpCommandHandler {
|
||||
@Resource private WvpDeviceService wvpDeviceService;
|
||||
|
||||
public R<?> handle(WvpDownRequest request) {
|
||||
return wvpDeviceService.loadCloudRecord(request);
|
||||
}
|
||||
|
||||
public String getSupportedCommand() {
|
||||
return CameraCommand.LOAD_CLOUD_RECORD.getFunctionName();
|
||||
}
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
package cn.wvp.protocol.handler.impl;
|
||||
|
||||
import cn.universal.common.constant.CameraCommand;
|
||||
import cn.universal.common.domain.R;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
import cn.wvp.protocol.handler.WvpCommandHandler;
|
||||
import cn.wvp.protocol.service.WvpDeviceService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class WvpPlaybackHandler implements WvpCommandHandler {
|
||||
@Resource private WvpDeviceService wvpDeviceService;
|
||||
|
||||
public R<?> handle(WvpDownRequest request) {
|
||||
return wvpDeviceService.startPlayback(request);
|
||||
}
|
||||
public String getSupportedCommand() { return CameraCommand.CAMERA_PLAYBACK.getFunctionName(); }
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
package cn.wvp.protocol.handler.impl;
|
||||
|
||||
import cn.universal.common.constant.CameraCommand;
|
||||
import cn.universal.common.domain.R;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
import cn.wvp.protocol.handler.WvpCommandHandler;
|
||||
import cn.wvp.protocol.service.WvpDeviceService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class WvpPtzControlHandler implements WvpCommandHandler {
|
||||
@Resource private WvpDeviceService wvpDeviceService;
|
||||
|
||||
public R<?> handle(WvpDownRequest request) {
|
||||
return wvpDeviceService.ptzControl(request);
|
||||
}
|
||||
public String getSupportedCommand() { return CameraCommand.CAMERA_TURN.getFunctionName(); }
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
package cn.wvp.protocol.handler.impl;
|
||||
|
||||
import cn.universal.common.constant.CameraCommand;
|
||||
import cn.universal.common.domain.R;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
import cn.wvp.protocol.handler.WvpCommandHandler;
|
||||
import cn.wvp.protocol.service.WvpDeviceService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* WVP查询云端录像处理器
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025/11/09
|
||||
*/
|
||||
@Component
|
||||
public class WvpQueryCloudRecordsHandler implements WvpCommandHandler {
|
||||
@Resource private WvpDeviceService wvpDeviceService;
|
||||
|
||||
public R<?> handle(WvpDownRequest request) {
|
||||
return wvpDeviceService.queryCloudRecords(request);
|
||||
}
|
||||
|
||||
public String getSupportedCommand() {
|
||||
return CameraCommand.QUERY_CLOUD_RECORDS.getFunctionName();
|
||||
}
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
package cn.wvp.protocol.handler.impl;
|
||||
|
||||
import cn.universal.common.constant.CameraCommand;
|
||||
import cn.universal.common.domain.R;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
import cn.wvp.protocol.handler.WvpCommandHandler;
|
||||
import cn.wvp.protocol.service.WvpDeviceService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* WVP查询录像处理器
|
||||
*
|
||||
* @version 1.0
|
||||
* @since 2025/11/09
|
||||
*/
|
||||
@Component
|
||||
public class WvpQueryRecordsHandler implements WvpCommandHandler {
|
||||
@Resource private WvpDeviceService wvpDeviceService;
|
||||
|
||||
public R<?> handle(WvpDownRequest request) {
|
||||
return wvpDeviceService.queryGBRecords(request);
|
||||
}
|
||||
|
||||
public String getSupportedCommand() {
|
||||
return CameraCommand.QUERY_RECORDS.getFunctionName();
|
||||
}
|
||||
}
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
package cn.wvp.protocol.handler.impl;
|
||||
|
||||
import cn.universal.common.constant.CameraCommand;
|
||||
import cn.universal.common.domain.R;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
import cn.wvp.protocol.handler.WvpCommandHandler;
|
||||
import cn.wvp.protocol.service.WvpDeviceService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class WvpSnapshotHandler implements WvpCommandHandler {
|
||||
@Resource private WvpDeviceService wvpDeviceService;
|
||||
|
||||
public R<?> handle(WvpDownRequest request) {
|
||||
return wvpDeviceService.snapshot(request);
|
||||
}
|
||||
|
||||
public String getSupportedCommand() {
|
||||
return CameraCommand.CAMERA_SNAPSHOT.getFunctionName();
|
||||
}
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
package cn.wvp.protocol.service;
|
||||
|
||||
import cn.universal.common.domain.R;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
import java.util.Map;
|
||||
|
||||
/** WVP 设备服务接口 */
|
||||
public interface WvpDeviceService {
|
||||
// 设备/组织/通道拉取(供缓存同步使用)
|
||||
R<?> listDevices(String instanceKey, Map<String, Object> filters);
|
||||
|
||||
R<?> listOrgs(String instanceKey, Map<String, Object> filters);
|
||||
|
||||
R<?> listChannels(String serial, Map<String, Object> filters);
|
||||
|
||||
// 能力调用(供命令处理器使用)
|
||||
R<?> ptzControl(WvpDownRequest request);
|
||||
|
||||
R<?> startPreview(WvpDownRequest request);
|
||||
|
||||
R<?> stopPreview(WvpDownRequest request);
|
||||
|
||||
R<?> startPlayback(WvpDownRequest request);
|
||||
|
||||
R<?> stopPlayback(WvpDownRequest request);
|
||||
|
||||
R<?> snapshot(WvpDownRequest request);
|
||||
|
||||
R<?> queryPresets(WvpDownRequest request);
|
||||
|
||||
// ==================== 国标录像相关 ====================
|
||||
|
||||
/** 查询国标录像 */
|
||||
R<?> queryGBRecords(WvpDownRequest request);
|
||||
|
||||
/** 开始下载国标录像 */
|
||||
R<?> startGBRecordDownload(WvpDownRequest request);
|
||||
|
||||
/** 停止下载国标录像 */
|
||||
R<?> stopGBRecordDownload(WvpDownRequest request);
|
||||
|
||||
/** 获取下载进度 */
|
||||
R<?> getGBRecordDownloadProgress(WvpDownRequest request);
|
||||
|
||||
// ==================== 云端录像相关 ====================
|
||||
|
||||
/** 查询云端录像日期列表 */
|
||||
R<?> queryCloudRecordDates(WvpDownRequest request);
|
||||
|
||||
/** 查询云端录像列表 */
|
||||
R<?> queryCloudRecords(WvpDownRequest request);
|
||||
|
||||
/** 加载云端录像文件 */
|
||||
R<?> loadCloudRecord(WvpDownRequest request);
|
||||
|
||||
/** 云端录像定位 */
|
||||
R<?> seekCloudRecord(WvpDownRequest request);
|
||||
|
||||
/** 设置云端录像倍速 */
|
||||
R<?> setCloudRecordSpeed(WvpDownRequest request);
|
||||
|
||||
/** 获取云端录像下载地址 */
|
||||
R<?> getCloudRecordPlayPath(WvpDownRequest request);
|
||||
}
|
||||
-175
@@ -1,175 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2025, NexIoT. All Rights Reserved.
|
||||
*
|
||||
* @Description: 本文件由 gitee.com/NexIoT 开发并拥有版权,未经授权严禁擅自商用、复制或传播。
|
||||
* @Author: gitee.com/NexIoT
|
||||
* @Email: wo8335224@gmail.com
|
||||
* @Wechat: outlookFil
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
package cn.wvp.protocol.service;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.universal.common.domain.R;
|
||||
import cn.universal.core.service.IDown;
|
||||
import cn.universal.dm.device.service.AbstractDownService;
|
||||
import cn.universal.persistence.dto.IoTDeviceDTO;
|
||||
import cn.universal.persistence.entity.IoTProduct;
|
||||
import cn.universal.persistence.query.IoTDeviceQuery;
|
||||
import cn.wvp.protocol.config.WvpModuleInfo;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
import cn.wvp.protocol.handle.WvpDownHandle;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* WVP下行服务
|
||||
*
|
||||
* @version 2.0
|
||||
* @since 2025/1/15
|
||||
*/
|
||||
@Service("wvpDownService")
|
||||
@Slf4j
|
||||
public class WvpDownService extends AbstractDownService<WvpDownRequest> implements IDown {
|
||||
|
||||
@Resource private WvpModuleInfo wvpModuleInfo;
|
||||
|
||||
@Resource private WvpDownHandle wvpDownHandle;
|
||||
|
||||
@Resource private cn.universal.core.downlink.DownlinkInterceptorChain downlinkInterceptorChain;
|
||||
|
||||
@Override
|
||||
public cn.universal.core.downlink.DownlinkInterceptorChain getInterceptorChain() {
|
||||
return downlinkInterceptorChain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String code() {
|
||||
return wvpModuleInfo.getCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return wvpModuleInfo.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public R<?> doProcess(cn.universal.core.downlink.DownlinkContext<?> context) {
|
||||
try {
|
||||
WvpDownRequest downRequest;
|
||||
if (context.getDownRequest() != null) {
|
||||
downRequest = (WvpDownRequest) context.getDownRequest();
|
||||
} else if (context.getRawMessage() != null) {
|
||||
downRequest = convert(context.getRawMessage());
|
||||
} else if (context.getJsonMessage() != null) {
|
||||
downRequest = convert(context.getJsonMessage());
|
||||
} else {
|
||||
return R.error("消息为空");
|
||||
}
|
||||
|
||||
if (downRequest == null) {
|
||||
return R.error("转换下行请求失败");
|
||||
}
|
||||
|
||||
// 设置上下文信息
|
||||
((cn.universal.core.downlink.DownlinkContext<WvpDownRequest>) context)
|
||||
.setDownRequest(downRequest);
|
||||
context.setProductKey(downRequest.getProductKey());
|
||||
context.setDeviceId(downRequest.getDeviceId());
|
||||
context.setIotId(downRequest.getIotId());
|
||||
|
||||
log.info(
|
||||
"[WVP下行] deviceId={} productKey={}",
|
||||
downRequest.getDeviceId(),
|
||||
downRequest.getProductKey());
|
||||
|
||||
// 执行处理
|
||||
return wvpDownHandle.down(downRequest);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("[WVP下行] 处理异常", e);
|
||||
return R.error("处理异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WvpDownRequest convert(String request) {
|
||||
return doConvert(request);
|
||||
}
|
||||
|
||||
private WvpDownRequest convert(JSONObject request) {
|
||||
return doConvert(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行转换操作
|
||||
*
|
||||
* @param request 请求对象
|
||||
* @return 转换后的下行请求
|
||||
*/
|
||||
private WvpDownRequest doConvert(Object request) {
|
||||
try {
|
||||
WvpDownRequest value = parseRequest(request);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
// 获取设备信息
|
||||
IoTDeviceDTO deviceDTO = getDeviceInfo(value);
|
||||
value.setIoTDeviceDTO(deviceDTO);
|
||||
// 获取产品信息
|
||||
IoTProduct product = getProduct(value.getProductKey());
|
||||
if (product == null) {
|
||||
log.warn("产品不存在: productKey={}", value.getProductKey());
|
||||
return null;
|
||||
}
|
||||
|
||||
// 设置相关信息
|
||||
value.setIoTProduct(product);
|
||||
value.setIoTDeviceDTO(deviceDTO);
|
||||
value.setWvpRequestData(value.getWvpRequestData());
|
||||
|
||||
return value;
|
||||
} catch (Exception e) {
|
||||
log.error("转换下行请求失败", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析请求对象
|
||||
*
|
||||
* @param request 请求对象
|
||||
* @return 解析后的下行请求
|
||||
*/
|
||||
private WvpDownRequest parseRequest(Object request) {
|
||||
if (request instanceof JSONObject) {
|
||||
return JSONUtil.toBean((JSONObject) request, WvpDownRequest.class);
|
||||
} else if (request instanceof String) {
|
||||
return JSONUtil.toBean((String) request, WvpDownRequest.class);
|
||||
} else {
|
||||
return JSONUtil.toBean(JSONUtil.toJsonStr(request), WvpDownRequest.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备信息
|
||||
*
|
||||
* @param value 下行请求
|
||||
* @return 设备信息
|
||||
*/
|
||||
private IoTDeviceDTO getDeviceInfo(WvpDownRequest value) {
|
||||
IoTDeviceQuery query =
|
||||
IoTDeviceQuery.builder()
|
||||
.productKey(value.getProductKey())
|
||||
.deviceId(value.getDeviceId())
|
||||
.iotId(value.getIotId())
|
||||
.build();
|
||||
return getIoTDeviceDTO(query);
|
||||
}
|
||||
}
|
||||
-873
@@ -1,873 +0,0 @@
|
||||
package cn.wvp.protocol.service.impl;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.universal.common.domain.R;
|
||||
import cn.wvp.protocol.constant.WvpApiConstants;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
import cn.wvp.protocol.service.WvpDeviceService;
|
||||
import cn.wvp.protocol.util.VideoPlatformHttpUtil;
|
||||
import java.util.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/** WVP设备服务实现 - 使用wvp-GB28181-pro标准接口 基于com.genersoft.iot.vmp.gb28181.controller下的标准API */
|
||||
@Slf4j
|
||||
@Service
|
||||
public class WvpDeviceServiceImpl implements WvpDeviceService {
|
||||
|
||||
/** 从filters中提取endpoint */
|
||||
private String getEndpoint(Map<String, Object> filters) {
|
||||
return String.valueOf(Optional.ofNullable(filters).map(m -> m.get("endpoint")).orElse(""));
|
||||
}
|
||||
|
||||
/** 从WvpDownRequest中提取endpoint */
|
||||
private String getEndpoint(WvpDownRequest request) {
|
||||
return String.valueOf(
|
||||
Optional.ofNullable(request.getWvpRequestData()).map(m -> m.get("endpoint")).orElse(""));
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> listDevices(String instanceKey, Map<String, Object> filters) {
|
||||
String endpoint = getEndpoint(filters);
|
||||
if (endpoint.isEmpty()) {
|
||||
return R.error("缺少参数: endpoint");
|
||||
}
|
||||
|
||||
JSONObject resp =
|
||||
VideoPlatformHttpUtil.httpGetJson(endpoint, WvpApiConstants.API_DEVICE_QUERY, filters);
|
||||
if (VideoPlatformHttpUtil.hasError(resp)) {
|
||||
return R.error("获取设备列表失败: " + VideoPlatformHttpUtil.getErrorMessage(resp));
|
||||
}
|
||||
|
||||
// 直接返回data部分,避免双层包装
|
||||
Object data = resp.get("data");
|
||||
return R.ok(data != null ? data : resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> listOrgs(String instanceKey, Map<String, Object> filters) {
|
||||
// WVP GB28181通常不提供组织树结构,返回空列表
|
||||
return R.ok(Collections.emptyList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> listChannels(String serial, Map<String, Object> filters) {
|
||||
String endpoint = getEndpoint(filters);
|
||||
if (endpoint.isEmpty()) {
|
||||
return R.error("缺少参数: endpoint");
|
||||
}
|
||||
|
||||
String path = String.format(WvpApiConstants.API_DEVICE_CHANNELS, serial);
|
||||
JSONObject resp = VideoPlatformHttpUtil.httpGetJson(endpoint, path, filters);
|
||||
if (VideoPlatformHttpUtil.hasError(resp)) {
|
||||
return R.error("获取通道列表失败: " + VideoPlatformHttpUtil.getErrorMessage(resp));
|
||||
}
|
||||
|
||||
// 直接返回data部分,避免双层包装
|
||||
Object data = resp.get("data");
|
||||
return R.ok(data != null ? data : resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> ptzControl(WvpDownRequest request) {
|
||||
JSONObject data = request.getData();
|
||||
if (data == null) {
|
||||
return R.error("缺少参数: data");
|
||||
}
|
||||
|
||||
String endpoint = getEndpoint(request);
|
||||
if (endpoint.isEmpty()) {
|
||||
return R.error("缺少参数: endpoint");
|
||||
}
|
||||
|
||||
String command = data.getStr("command"); // left/right/up/down/stop/zoomin/zoomout/focusIn/focusOut/irisIn/irisOut等
|
||||
String channelId = data.getStr("channelId");
|
||||
Integer speed = data.getInt("speed", 50); // 速度默认50
|
||||
|
||||
if (command == null || command.isEmpty()) {
|
||||
return R.error("缺少参数: command");
|
||||
}
|
||||
if (channelId == null || channelId.isEmpty()) {
|
||||
return R.error("缺少参数: channelId");
|
||||
}
|
||||
|
||||
// 根据命令类型选择不同的接口
|
||||
String path;
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
if (request.getWvpRequestData() != null) {
|
||||
filters.putAll(request.getWvpRequestData());
|
||||
}
|
||||
|
||||
// 判断是否为聚焦命令
|
||||
if ("focusIn".equalsIgnoreCase(command) || "focusOut".equalsIgnoreCase(command)) {
|
||||
path = String.format(WvpApiConstants.API_FOCUS_CONTROL, request.getDeviceId(), channelId);
|
||||
// 转换命令: focusIn -> near, focusOut -> far
|
||||
String focusCmd = "focusIn".equalsIgnoreCase(command) ? "near" : "far";
|
||||
filters.put("command", focusCmd);
|
||||
filters.put("speed", speed);
|
||||
}
|
||||
// 判断是否为光圈命令
|
||||
else if ("irisIn".equalsIgnoreCase(command) || "irisOut".equalsIgnoreCase(command)) {
|
||||
path = String.format(WvpApiConstants.API_IRIS_CONTROL, request.getDeviceId(), channelId);
|
||||
// 转换命令: irisIn -> in, irisOut -> out
|
||||
String irisCmd = "irisIn".equalsIgnoreCase(command) ? "in" : "out";
|
||||
filters.put("command", irisCmd);
|
||||
filters.put("speed", speed);
|
||||
}
|
||||
// 云台方向和缩放命令
|
||||
else {
|
||||
path = String.format(WvpApiConstants.API_PTZ_CONTROL, request.getDeviceId(), channelId);
|
||||
|
||||
// 计算horizonSpeed和verticalSpeed
|
||||
int horizonSpeed = 0;
|
||||
int verticalSpeed = 0;
|
||||
int zoomSpeed = 0;
|
||||
|
||||
// 根据命令设置速度
|
||||
switch (command.toLowerCase()) {
|
||||
case "left":
|
||||
case "right":
|
||||
horizonSpeed = speed;
|
||||
break;
|
||||
case "up":
|
||||
case "down":
|
||||
verticalSpeed = speed;
|
||||
break;
|
||||
case "leftup":
|
||||
case "upleft":
|
||||
case "rightup":
|
||||
case "upright":
|
||||
case "leftdown":
|
||||
case "downleft":
|
||||
case "rightdown":
|
||||
case "downright":
|
||||
horizonSpeed = speed;
|
||||
verticalSpeed = speed;
|
||||
break;
|
||||
case "zoomin":
|
||||
case "zoomout":
|
||||
zoomSpeed = Math.min(speed / 16, 15); // zoomSpeed范围0-15
|
||||
break;
|
||||
case "stop":
|
||||
// stop命令速度都为0
|
||||
break;
|
||||
default:
|
||||
return R.error("不支持的命令: " + command);
|
||||
}
|
||||
|
||||
filters.put("command", command.toLowerCase());
|
||||
filters.put("horizonSpeed", horizonSpeed);
|
||||
filters.put("verticalSpeed", verticalSpeed);
|
||||
filters.put("zoomSpeed", zoomSpeed);
|
||||
}
|
||||
|
||||
JSONObject resp = VideoPlatformHttpUtil.httpGetJson(endpoint, path, filters);
|
||||
|
||||
if (VideoPlatformHttpUtil.hasError(resp)) {
|
||||
return R.error("云台控制失败: " + VideoPlatformHttpUtil.getErrorMessage(resp));
|
||||
}
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("executed", true);
|
||||
result.put("command", command);
|
||||
result.put("channelId", channelId);
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> startPreview(WvpDownRequest request) {
|
||||
JSONObject data = request.getData();
|
||||
String endpoint = getEndpoint(request);
|
||||
if (endpoint.isEmpty()) {
|
||||
return R.error("缺少参数: endpoint");
|
||||
}
|
||||
|
||||
String channelId = data == null ? null : data.getStr("channelId");
|
||||
if (channelId == null || channelId.isEmpty()) {
|
||||
return R.error("缺少参数: channelId");
|
||||
}
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
if (request.getWvpRequestData() != null) {
|
||||
filters.putAll(request.getWvpRequestData());
|
||||
}
|
||||
filters.put("channelId", channelId);
|
||||
|
||||
String path = String.format(WvpApiConstants.API_PLAY_START, request.getDeviceId(), channelId);
|
||||
JSONObject resp = VideoPlatformHttpUtil.httpGetJson(endpoint, path, filters);
|
||||
|
||||
if (VideoPlatformHttpUtil.hasError(resp)) {
|
||||
return R.error("启动预览失败: " + VideoPlatformHttpUtil.getErrorMessage(resp));
|
||||
}
|
||||
return R.ok(resp.getJSONObject("data"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> startPlayback(WvpDownRequest request) {
|
||||
JSONObject data = request.getData();
|
||||
String endpoint = getEndpoint(request);
|
||||
if (endpoint.isEmpty()) {
|
||||
return R.error("缺少参数: endpoint");
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
return R.error("缺少参数: data");
|
||||
}
|
||||
|
||||
String channelId = data.getStr("channelId");
|
||||
Object startTimeObj = data.get("startTime");
|
||||
Object endTimeObj = data.get("endTime");
|
||||
|
||||
if (channelId == null || startTimeObj == null || endTimeObj == null) {
|
||||
return R.error("缺少参数: channelId/startTime/endTime");
|
||||
}
|
||||
|
||||
// 将时间戳转换为时间字符串格式(wvp API 需要 yyyy-MM-dd HH:mm:ss 格式)
|
||||
String startTimeStr;
|
||||
String endTimeStr;
|
||||
try {
|
||||
long startTime;
|
||||
long endTime;
|
||||
|
||||
// 处理时间参数:可能是 Long 类型的时间戳,也可能是 String 类型
|
||||
if (startTimeObj instanceof Long) {
|
||||
startTime = (Long) startTimeObj;
|
||||
} else if (startTimeObj instanceof String) {
|
||||
startTime = Long.parseLong((String) startTimeObj);
|
||||
} else {
|
||||
startTime = ((Number) startTimeObj).longValue();
|
||||
}
|
||||
|
||||
if (endTimeObj instanceof Long) {
|
||||
endTime = (Long) endTimeObj;
|
||||
} else if (endTimeObj instanceof String) {
|
||||
endTime = Long.parseLong((String) endTimeObj);
|
||||
} else {
|
||||
endTime = ((Number) endTimeObj).longValue();
|
||||
}
|
||||
|
||||
// 如果是10位秒级时间戳,转换为毫秒
|
||||
if (startTime > 0 && startTime < 10000000000L) {
|
||||
startTime = startTime * 1000;
|
||||
}
|
||||
if (endTime > 0 && endTime < 10000000000L) {
|
||||
endTime = endTime * 1000;
|
||||
}
|
||||
|
||||
// 转换为时间字符串格式
|
||||
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
startTimeStr = sdf.format(new java.util.Date(startTime));
|
||||
endTimeStr = sdf.format(new java.util.Date(endTime));
|
||||
} catch (Exception e) {
|
||||
return R.error("时间参数格式错误: " + e.getMessage());
|
||||
}
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
if (request.getWvpRequestData() != null) {
|
||||
filters.putAll(request.getWvpRequestData());
|
||||
}
|
||||
// wvp API 需要时间字符串格式
|
||||
filters.put("startTime", startTimeStr);
|
||||
filters.put("endTime", endTimeStr);
|
||||
|
||||
String path =
|
||||
String.format(WvpApiConstants.API_PLAYBACK_START, request.getDeviceId(), channelId);
|
||||
JSONObject resp = VideoPlatformHttpUtil.httpGetJson(endpoint, path, filters);
|
||||
|
||||
if (VideoPlatformHttpUtil.hasError(resp)) {
|
||||
return R.error("启动回放失败: " + VideoPlatformHttpUtil.getErrorMessage(resp));
|
||||
}
|
||||
|
||||
// 从响应中获取 data 对象
|
||||
JSONObject dataObj = resp.getJSONObject("data");
|
||||
if (dataObj == null) {
|
||||
return R.error("启动回放失败: 响应数据为空");
|
||||
}
|
||||
|
||||
// 提取所有流地址字段
|
||||
Map<String, String> urls = new LinkedHashMap<>();
|
||||
String flv = dataObj.getStr("flv");
|
||||
String httpsFlv = dataObj.getStr("https_flv");
|
||||
String wsFlv = dataObj.getStr("ws_flv");
|
||||
String wssFlv = dataObj.getStr("wss_flv");
|
||||
String hls = dataObj.getStr("hls");
|
||||
String httpsHls = dataObj.getStr("https_hls");
|
||||
String wsHls = dataObj.getStr("ws_hls");
|
||||
String wssHls = dataObj.getStr("wss_hls");
|
||||
String rtmp = dataObj.getStr("rtmp");
|
||||
String rtmps = dataObj.getStr("rtmps");
|
||||
String rtsp = dataObj.getStr("rtsp");
|
||||
String rtsps = dataObj.getStr("rtsps");
|
||||
|
||||
if (flv != null) urls.put("flv", flv);
|
||||
if (httpsFlv != null) urls.put("https_flv", httpsFlv);
|
||||
if (wsFlv != null) urls.put("ws_flv", wsFlv);
|
||||
if (wssFlv != null) urls.put("wss_flv", wssFlv);
|
||||
if (hls != null) urls.put("hls", hls);
|
||||
if (httpsHls != null) urls.put("https_hls", httpsHls);
|
||||
if (wsHls != null) urls.put("ws_hls", wsHls);
|
||||
if (wssHls != null) urls.put("wss_hls", wssHls);
|
||||
if (rtmp != null) urls.put("rtmp", rtmp);
|
||||
if (rtmps != null) urls.put("rtmps", rtmps);
|
||||
if (rtsp != null) urls.put("rtsp", rtsp);
|
||||
if (rtsps != null) urls.put("rtsps", rtsps);
|
||||
|
||||
// 提取其他重要字段
|
||||
String app = dataObj.getStr("app");
|
||||
String stream = dataObj.getStr("stream");
|
||||
String mediaServerId = dataObj.getStr("mediaServerId");
|
||||
String key = dataObj.getStr("key");
|
||||
String respStartTimeStr = dataObj.getStr("startTime");
|
||||
String respEndTimeStr = dataObj.getStr("endTime");
|
||||
|
||||
Map<String, Object> res = new HashMap<>();
|
||||
res.put("channelId", channelId);
|
||||
|
||||
// 保存原始时间戳(毫秒)- 使用请求时的时间字符串
|
||||
try {
|
||||
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
res.put("startTime", sdf.parse(startTimeStr).getTime());
|
||||
res.put("endTime", sdf.parse(endTimeStr).getTime());
|
||||
} catch (Exception e) {
|
||||
// 如果解析失败,使用当前时间
|
||||
res.put("startTime", System.currentTimeMillis());
|
||||
res.put("endTime", System.currentTimeMillis());
|
||||
}
|
||||
|
||||
// 将所有流地址直接放在根级别,而不是嵌套在 urls 中(前端需要直接访问)
|
||||
res.putAll(urls);
|
||||
|
||||
// 添加其他重要字段
|
||||
if (app != null) res.put("app", app);
|
||||
if (stream != null) res.put("stream", stream);
|
||||
if (mediaServerId != null) res.put("mediaServerId", mediaServerId);
|
||||
if (key != null) res.put("key", key);
|
||||
// 使用响应中的时间字符串(如果存在)
|
||||
if (respStartTimeStr != null) res.put("startTimeStr", respStartTimeStr);
|
||||
else if (startTimeStr != null) res.put("startTimeStr", startTimeStr);
|
||||
if (respEndTimeStr != null) res.put("endTimeStr", respEndTimeStr);
|
||||
else if (endTimeStr != null) res.put("endTimeStr", endTimeStr);
|
||||
|
||||
return R.ok(res);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> snapshot(WvpDownRequest request) {
|
||||
JSONObject data = request.getData();
|
||||
String endpoint = getEndpoint(request);
|
||||
if (endpoint.isEmpty()) {
|
||||
return R.error("缺少参数: endpoint");
|
||||
}
|
||||
|
||||
String channelId = data == null ? null : data.getStr("channelId");
|
||||
if (channelId == null) {
|
||||
return R.error("缺少参数: channelId");
|
||||
}
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
if (request.getWvpRequestData() != null) {
|
||||
filters.putAll(request.getWvpRequestData());
|
||||
}
|
||||
filters.put("channelId", channelId);
|
||||
filters.put("deviceId", request.getDeviceId());
|
||||
String path = String.format(WvpApiConstants.API_SNAPSHOT, request.getDeviceId(), channelId);
|
||||
JSONObject resp = VideoPlatformHttpUtil.httpGetJson(endpoint, path, filters);
|
||||
|
||||
if (VideoPlatformHttpUtil.hasError(resp)) {
|
||||
return R.error("抓拍失败: " + VideoPlatformHttpUtil.getErrorMessage(resp));
|
||||
}
|
||||
|
||||
String snapshotUrl = resp.getStr("snap", resp.getStr("url", ""));
|
||||
Map<String, Object> res = new HashMap<>();
|
||||
res.put("channelId", channelId);
|
||||
res.put("snapshotUrl", snapshotUrl);
|
||||
return R.ok(res);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> stopPreview(WvpDownRequest request) {
|
||||
JSONObject data = request.getData();
|
||||
String endpoint = getEndpoint(request);
|
||||
if (endpoint.isEmpty()) {
|
||||
return R.error("缺少参数: endpoint");
|
||||
}
|
||||
|
||||
String channelId = data == null ? null : data.getStr("channelId");
|
||||
if (channelId == null || channelId.isEmpty()) {
|
||||
return R.error("缺少参数: channelId");
|
||||
}
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
if (request.getWvpRequestData() != null) {
|
||||
filters.putAll(request.getWvpRequestData());
|
||||
}
|
||||
|
||||
String path = String.format(WvpApiConstants.API_PLAY_STOP, request.getDeviceId(), channelId);
|
||||
JSONObject resp = VideoPlatformHttpUtil.httpGetJson(endpoint, path, filters);
|
||||
|
||||
if (VideoPlatformHttpUtil.hasError(resp)) {
|
||||
return R.error("停止预览失败: " + VideoPlatformHttpUtil.getErrorMessage(resp));
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> queryPresets(WvpDownRequest request) {
|
||||
JSONObject data = request.getData();
|
||||
String endpoint = getEndpoint(request);
|
||||
if (endpoint.isEmpty()) {
|
||||
return R.error("缺少参数: endpoint");
|
||||
}
|
||||
|
||||
String channelId = data == null ? null : data.getStr("channelId");
|
||||
if (channelId == null || channelId.isEmpty()) {
|
||||
return R.error("缺少参数: channelId");
|
||||
}
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
if (request.getWvpRequestData() != null) {
|
||||
filters.putAll(request.getWvpRequestData());
|
||||
}
|
||||
|
||||
String path = String.format(WvpApiConstants.API_PRESET_QUERY, request.getDeviceId(), channelId);
|
||||
JSONObject resp = VideoPlatformHttpUtil.httpGetJson(endpoint, path, filters);
|
||||
|
||||
if (VideoPlatformHttpUtil.hasError(resp)) {
|
||||
return R.error("查询预置位失败: " + VideoPlatformHttpUtil.getErrorMessage(resp));
|
||||
}
|
||||
|
||||
return R.ok(resp);
|
||||
}
|
||||
|
||||
// ==================== 以下为新增录像相关接口 ====================
|
||||
|
||||
@Override
|
||||
public R<?> stopPlayback(WvpDownRequest request) {
|
||||
JSONObject data = request.getData();
|
||||
String endpoint = getEndpoint(request);
|
||||
if (endpoint.isEmpty()) {
|
||||
return R.error("缺少参数: endpoint");
|
||||
}
|
||||
|
||||
String channelId = data == null ? null : data.getStr("channelId");
|
||||
if (channelId == null || channelId.isEmpty()) {
|
||||
return R.error("缺岑参数: channelId");
|
||||
}
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
if (request.getWvpRequestData() != null) {
|
||||
filters.putAll(request.getWvpRequestData());
|
||||
}
|
||||
|
||||
String path =
|
||||
String.format(WvpApiConstants.API_PLAYBACK_STOP, request.getDeviceId(), channelId);
|
||||
JSONObject resp = VideoPlatformHttpUtil.httpGetJson(endpoint, path, filters);
|
||||
|
||||
if (VideoPlatformHttpUtil.hasError(resp)) {
|
||||
return R.error("停止回放失败: " + VideoPlatformHttpUtil.getErrorMessage(resp));
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> queryGBRecords(WvpDownRequest request) {
|
||||
JSONObject data = request.getData();
|
||||
String endpoint = getEndpoint(request);
|
||||
if (endpoint.isEmpty()) {
|
||||
return R.error("缺岑参数: endpoint");
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
return R.error("缺岑参数: data");
|
||||
}
|
||||
|
||||
String channelId = data.getStr("channelId");
|
||||
String startTime = data.getStr("startTime");
|
||||
String endTime = data.getStr("endTime");
|
||||
|
||||
if (channelId == null || startTime == null || endTime == null) {
|
||||
return R.error("缺岑参数: channelId/startTime/endTime");
|
||||
}
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
if (request.getWvpRequestData() != null) {
|
||||
filters.putAll(request.getWvpRequestData());
|
||||
}
|
||||
filters.put("startTime", startTime);
|
||||
filters.put("endTime", endTime);
|
||||
|
||||
String path =
|
||||
String.format(WvpApiConstants.API_GB_RECORD_QUERY, request.getDeviceId(), channelId);
|
||||
JSONObject resp = VideoPlatformHttpUtil.httpGetJson(endpoint, path, filters);
|
||||
|
||||
if (VideoPlatformHttpUtil.hasError(resp)) {
|
||||
return R.error("国标录像查询失败: " + VideoPlatformHttpUtil.getErrorMessage(resp));
|
||||
}
|
||||
|
||||
return R.ok(resp.getJSONObject("data"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> startGBRecordDownload(WvpDownRequest request) {
|
||||
JSONObject data = request.getData();
|
||||
String endpoint = getEndpoint(request);
|
||||
if (endpoint.isEmpty()) {
|
||||
return R.error("缺岑参数: endpoint");
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
return R.error("缺岑参数: data");
|
||||
}
|
||||
|
||||
String channelId = data.getStr("channelId");
|
||||
String startTime = data.getStr("startTime");
|
||||
String endTime = data.getStr("endTime");
|
||||
Integer downloadSpeed = data.getInt("downloadSpeed", 1);
|
||||
|
||||
if (channelId == null || startTime == null || endTime == null) {
|
||||
return R.error("缺岑参数: channelId/startTime/endTime");
|
||||
}
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
if (request.getWvpRequestData() != null) {
|
||||
filters.putAll(request.getWvpRequestData());
|
||||
}
|
||||
filters.put("startTime", startTime);
|
||||
filters.put("endTime", endTime);
|
||||
filters.put("downloadSpeed", downloadSpeed);
|
||||
|
||||
String path =
|
||||
String.format(
|
||||
WvpApiConstants.API_GB_RECORD_DOWNLOAD_START, request.getDeviceId(), channelId);
|
||||
JSONObject resp = VideoPlatformHttpUtil.httpGetJson(endpoint, path, filters);
|
||||
|
||||
if (VideoPlatformHttpUtil.hasError(resp)) {
|
||||
return R.error("开始下载失败: " + VideoPlatformHttpUtil.getErrorMessage(resp));
|
||||
}
|
||||
|
||||
return R.ok(resp.getJSONObject("data"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> stopGBRecordDownload(WvpDownRequest request) {
|
||||
JSONObject data = request.getData();
|
||||
String endpoint = getEndpoint(request);
|
||||
if (endpoint.isEmpty()) {
|
||||
return R.error("缺岑参数: endpoint");
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
return R.error("缺岑参数: data");
|
||||
}
|
||||
|
||||
String channelId = data.getStr("channelId");
|
||||
String stream = data.getStr("stream");
|
||||
|
||||
if (channelId == null || stream == null) {
|
||||
return R.error("缺岑参数: channelId/stream");
|
||||
}
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
if (request.getWvpRequestData() != null) {
|
||||
filters.putAll(request.getWvpRequestData());
|
||||
}
|
||||
|
||||
String path =
|
||||
String.format(
|
||||
WvpApiConstants.API_GB_RECORD_DOWNLOAD_STOP, request.getDeviceId(), channelId, stream);
|
||||
JSONObject resp = VideoPlatformHttpUtil.httpGetJson(endpoint, path, filters);
|
||||
|
||||
if (VideoPlatformHttpUtil.hasError(resp)) {
|
||||
return R.error("停止下载失败: " + VideoPlatformHttpUtil.getErrorMessage(resp));
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> getGBRecordDownloadProgress(WvpDownRequest request) {
|
||||
JSONObject data = request.getData();
|
||||
String endpoint = getEndpoint(request);
|
||||
if (endpoint.isEmpty()) {
|
||||
return R.error("缺岑参数: endpoint");
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
return R.error("缺岑参数: data");
|
||||
}
|
||||
|
||||
String channelId = data.getStr("channelId");
|
||||
String stream = data.getStr("stream");
|
||||
|
||||
if (channelId == null || stream == null) {
|
||||
return R.error("缺岑参数: channelId/stream");
|
||||
}
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
if (request.getWvpRequestData() != null) {
|
||||
filters.putAll(request.getWvpRequestData());
|
||||
}
|
||||
|
||||
String path =
|
||||
String.format(
|
||||
WvpApiConstants.API_GB_RECORD_DOWNLOAD_PROGRESS,
|
||||
request.getDeviceId(),
|
||||
channelId,
|
||||
stream);
|
||||
JSONObject resp = VideoPlatformHttpUtil.httpGetJson(endpoint, path, filters);
|
||||
|
||||
if (VideoPlatformHttpUtil.hasError(resp)) {
|
||||
return R.error("获取下载进度失败: " + VideoPlatformHttpUtil.getErrorMessage(resp));
|
||||
}
|
||||
|
||||
// 直接返回data部分,避免双层包装
|
||||
Object respData = resp.get("data");
|
||||
return R.ok(respData != null ? respData : resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> queryCloudRecordDates(WvpDownRequest request) {
|
||||
JSONObject data = request.getData();
|
||||
String endpoint = getEndpoint(request);
|
||||
if (endpoint.isEmpty()) {
|
||||
return R.error("缺岑参数: endpoint");
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
return R.error("缺岑参数: data");
|
||||
}
|
||||
|
||||
String app = data.getStr("app");
|
||||
String stream = data.getStr("stream");
|
||||
|
||||
if (app == null || stream == null) {
|
||||
return R.error("缺岑参数: app/stream");
|
||||
}
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
if (request.getWvpRequestData() != null) {
|
||||
filters.putAll(request.getWvpRequestData());
|
||||
}
|
||||
filters.put("app", app);
|
||||
filters.put("stream", stream);
|
||||
|
||||
// 可选参数
|
||||
if (data.containsKey("year")) filters.put("year", data.getInt("year"));
|
||||
if (data.containsKey("month")) filters.put("month", data.getInt("month"));
|
||||
if (data.containsKey("mediaServerId"))
|
||||
filters.put("mediaServerId", data.getStr("mediaServerId"));
|
||||
|
||||
JSONObject resp =
|
||||
VideoPlatformHttpUtil.httpGetJson(
|
||||
endpoint, WvpApiConstants.API_CLOUD_RECORD_DATE_LIST, filters);
|
||||
|
||||
if (VideoPlatformHttpUtil.hasError(resp)) {
|
||||
return R.error("查询云端录像日期失败: " + VideoPlatformHttpUtil.getErrorMessage(resp));
|
||||
}
|
||||
|
||||
// 直接返回data部分,避免双层包装
|
||||
Object respData = resp.get("data");
|
||||
return R.ok(respData != null ? respData : resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> queryCloudRecords(WvpDownRequest request) {
|
||||
JSONObject data = request.getData();
|
||||
String endpoint = getEndpoint(request);
|
||||
if (endpoint.isEmpty()) {
|
||||
return R.error("缺岑参数: endpoint");
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
return R.error("缺岑参数: data");
|
||||
}
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
if (request.getWvpRequestData() != null) {
|
||||
filters.putAll(request.getWvpRequestData());
|
||||
}
|
||||
|
||||
// 必填分页参数
|
||||
filters.put("page", data.getInt("page", 1));
|
||||
filters.put("count", data.getInt("count", 10));
|
||||
|
||||
// 可选参数
|
||||
if (data.containsKey("app")) filters.put("app", data.getStr("app"));
|
||||
if (data.containsKey("stream")) filters.put("stream", data.getStr("stream"));
|
||||
if (data.containsKey("startTime")) filters.put("startTime", data.getStr("startTime"));
|
||||
if (data.containsKey("endTime")) filters.put("endTime", data.getStr("endTime"));
|
||||
if (data.containsKey("mediaServerId"))
|
||||
filters.put("mediaServerId", data.getStr("mediaServerId"));
|
||||
if (data.containsKey("callId")) filters.put("callId", data.getStr("callId"));
|
||||
if (data.containsKey("query")) filters.put("query", data.getStr("query"));
|
||||
|
||||
JSONObject resp =
|
||||
VideoPlatformHttpUtil.httpGetJson(endpoint, WvpApiConstants.API_CLOUD_RECORD_LIST, filters);
|
||||
|
||||
if (VideoPlatformHttpUtil.hasError(resp)) {
|
||||
return R.error("查询云端录像列表失败: " + VideoPlatformHttpUtil.getErrorMessage(resp));
|
||||
}
|
||||
|
||||
// 直接返回data部分,避免双层包装
|
||||
Object respData = resp.get("data");
|
||||
return R.ok(respData != null ? respData : resp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> loadCloudRecord(WvpDownRequest request) {
|
||||
JSONObject data = request.getData();
|
||||
String endpoint = getEndpoint(request);
|
||||
if (endpoint.isEmpty()) {
|
||||
return R.error("缺岑参数: endpoint");
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
return R.error("缺岑参数: data");
|
||||
}
|
||||
|
||||
String app = data.getStr("app");
|
||||
String stream = data.getStr("stream");
|
||||
Integer cloudRecordId = data.getInt("cloudRecordId");
|
||||
|
||||
if (app == null || stream == null || cloudRecordId == null) {
|
||||
return R.error("缺岑参数: app/stream/cloudRecordId");
|
||||
}
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
if (request.getWvpRequestData() != null) {
|
||||
filters.putAll(request.getWvpRequestData());
|
||||
}
|
||||
filters.put("app", app);
|
||||
filters.put("stream", stream);
|
||||
filters.put("cloudRecordId", cloudRecordId);
|
||||
|
||||
JSONObject resp =
|
||||
VideoPlatformHttpUtil.httpGetJson(endpoint, WvpApiConstants.API_CLOUD_RECORD_LOAD, filters);
|
||||
|
||||
if (VideoPlatformHttpUtil.hasError(resp)) {
|
||||
return R.error("加载云端录像失败: " + VideoPlatformHttpUtil.getErrorMessage(resp));
|
||||
}
|
||||
|
||||
return R.ok(resp.getJSONObject("data"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> seekCloudRecord(WvpDownRequest request) {
|
||||
JSONObject data = request.getData();
|
||||
String endpoint = getEndpoint(request);
|
||||
if (endpoint.isEmpty()) {
|
||||
return R.error("缺岑参数: endpoint");
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
return R.error("缺岑参数: data");
|
||||
}
|
||||
|
||||
String mediaServerId = data.getStr("mediaServerId");
|
||||
String app = data.getStr("app");
|
||||
String stream = data.getStr("stream");
|
||||
Double seek = data.getDouble("seek");
|
||||
|
||||
if (mediaServerId == null || app == null || stream == null || seek == null) {
|
||||
return R.error("缺岑参数: mediaServerId/app/stream/seek");
|
||||
}
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
if (request.getWvpRequestData() != null) {
|
||||
filters.putAll(request.getWvpRequestData());
|
||||
}
|
||||
filters.put("mediaServerId", mediaServerId);
|
||||
filters.put("app", app);
|
||||
filters.put("stream", stream);
|
||||
filters.put("seek", seek);
|
||||
if (data.containsKey("schema")) filters.put("schema", data.getStr("schema"));
|
||||
|
||||
JSONObject resp =
|
||||
VideoPlatformHttpUtil.httpGetJson(endpoint, WvpApiConstants.API_CLOUD_RECORD_SEEK, filters);
|
||||
|
||||
if (VideoPlatformHttpUtil.hasError(resp)) {
|
||||
return R.error("云端录像定位失败: " + VideoPlatformHttpUtil.getErrorMessage(resp));
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> setCloudRecordSpeed(WvpDownRequest request) {
|
||||
JSONObject data = request.getData();
|
||||
String endpoint = getEndpoint(request);
|
||||
if (endpoint.isEmpty()) {
|
||||
return R.error("缺岑参数: endpoint");
|
||||
}
|
||||
|
||||
if (data == null) {
|
||||
return R.error("缺岑参数: data");
|
||||
}
|
||||
|
||||
String mediaServerId = data.getStr("mediaServerId");
|
||||
String app = data.getStr("app");
|
||||
String stream = data.getStr("stream");
|
||||
Integer speed = data.getInt("speed");
|
||||
|
||||
if (mediaServerId == null || app == null || stream == null || speed == null) {
|
||||
return R.error("缺岑参数: mediaServerId/app/stream/speed");
|
||||
}
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
if (request.getWvpRequestData() != null) {
|
||||
filters.putAll(request.getWvpRequestData());
|
||||
}
|
||||
filters.put("mediaServerId", mediaServerId);
|
||||
filters.put("app", app);
|
||||
filters.put("stream", stream);
|
||||
filters.put("speed", speed);
|
||||
if (data.containsKey("schema")) filters.put("schema", data.getStr("schema"));
|
||||
|
||||
JSONObject resp =
|
||||
VideoPlatformHttpUtil.httpGetJson(
|
||||
endpoint, WvpApiConstants.API_CLOUD_RECORD_SPEED, filters);
|
||||
|
||||
if (VideoPlatformHttpUtil.hasError(resp)) {
|
||||
return R.error("设置云端录像倍速失败: " + VideoPlatformHttpUtil.getErrorMessage(resp));
|
||||
}
|
||||
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> getCloudRecordPlayPath(WvpDownRequest request) {
|
||||
JSONObject data = request.getData();
|
||||
String endpoint = getEndpoint(request);
|
||||
if (endpoint.isEmpty()) {
|
||||
return R.error("缺岑参数: endpoint");
|
||||
}
|
||||
|
||||
if (data == null || !data.containsKey("recordId")) {
|
||||
return R.error("缺岑参数: recordId");
|
||||
}
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
if (request.getWvpRequestData() != null) {
|
||||
filters.putAll(request.getWvpRequestData());
|
||||
}
|
||||
filters.put("recordId", data.getInt("recordId"));
|
||||
|
||||
JSONObject resp =
|
||||
VideoPlatformHttpUtil.httpGetJson(
|
||||
endpoint, WvpApiConstants.API_CLOUD_RECORD_PLAY_PATH, filters);
|
||||
|
||||
if (VideoPlatformHttpUtil.hasError(resp)) {
|
||||
return R.error("获取云端录像下载地址失败: " + VideoPlatformHttpUtil.getErrorMessage(resp));
|
||||
}
|
||||
|
||||
// 解析WVP返回的data字段,避免双重包裹
|
||||
JSONObject result = resp.getJSONObject("data");
|
||||
if (result == null) {
|
||||
result = new JSONObject();
|
||||
}
|
||||
|
||||
return R.ok(result);
|
||||
}
|
||||
}
|
||||
-270
@@ -1,270 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025, NexIoT. All Rights Reserved.
|
||||
*/
|
||||
package cn.wvp.protocol.util;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 视频平台HTTP请求工具类
|
||||
*/
|
||||
@Slf4j
|
||||
public class VideoPlatformHttpUtil {
|
||||
|
||||
private static final int DEFAULT_TIMEOUT = 10000; // 10秒超时
|
||||
|
||||
/**
|
||||
* 执行GET请求并返回JSON结果(自动从filters提取token)
|
||||
*
|
||||
* @param baseUrl 基础URL
|
||||
* @param path 接口路径
|
||||
* @param filters 过滤参数(包含token)
|
||||
* @return JSON响应对象
|
||||
*/
|
||||
public static JSONObject httpGetJson(
|
||||
String baseUrl, String path, Map<String, Object> filters) {
|
||||
return httpGetJson(baseUrl, path, filters, DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行GET请求并返回JSON结果(支持自定义超时)
|
||||
*
|
||||
* @param baseUrl 基础URL
|
||||
* @param path 接口路径
|
||||
* @param filters 过滤参数(包含token和其他查询参数)
|
||||
* @param timeout 超时时间(毫秒)
|
||||
* @return JSON响应对象
|
||||
*/
|
||||
public static JSONObject httpGetJson(
|
||||
String baseUrl,
|
||||
String path,
|
||||
Map<String, Object> filters,
|
||||
int timeout) {
|
||||
String url = buildUrl(baseUrl, path);
|
||||
HttpRequest request = HttpRequest.get(url).timeout(timeout);
|
||||
|
||||
// 提取并设置token
|
||||
String token = extractToken(filters);
|
||||
if (token != null && !token.isEmpty()) {
|
||||
request.header("Access-Token", token);
|
||||
}
|
||||
|
||||
// 添加查询参数(排除token和endpoint/auth等元数据)
|
||||
Map<String, Object> params = buildQueryParams(filters);
|
||||
if (!params.isEmpty()) {
|
||||
request.form(params);
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("HTTP GET请求: URL={}, params={}", url, filters);
|
||||
HttpResponse response = request.execute();
|
||||
String body = response.body();
|
||||
log.info("x`HTTP GET响应: status={}, body={}", response.getStatus(), body);
|
||||
|
||||
if (body == null || body.isEmpty()) {
|
||||
return buildErrorResponse("响应体为空");
|
||||
}
|
||||
return JSONUtil.parseObj(body);
|
||||
} catch (Exception e) {
|
||||
log.error("HTTP GET请求失败: URL={}, error={}", url, e.getMessage(), e);
|
||||
return buildErrorResponse(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行POST请求并返回JSON结果(自动从filters提取token)
|
||||
*
|
||||
* @param baseUrl 基础URL
|
||||
* @param path 接口路径
|
||||
* @param body 请求体(JSON)
|
||||
* @param filters 过滤参数(包含token)
|
||||
* @return JSON响应对象
|
||||
*/
|
||||
public static JSONObject httpPostJson(
|
||||
String baseUrl, String path, JSONObject body, Map<String, Object> filters) {
|
||||
return httpPostJson(baseUrl, path, body, filters, DEFAULT_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行POST请求并返回JSON结果(支持自定义超时)
|
||||
*
|
||||
* @param baseUrl 基础URL
|
||||
* @param path 接口路径
|
||||
* @param body 请求体(JSON)
|
||||
* @param filters 过滤参数(包含token)
|
||||
* @param timeout 超时时间(毫秒)
|
||||
* @return JSON响应对象
|
||||
*/
|
||||
public static JSONObject httpPostJson(
|
||||
String baseUrl, String path, JSONObject body, Map<String, Object> filters, int timeout) {
|
||||
String url = buildUrl(baseUrl, path);
|
||||
HttpRequest request = HttpRequest.post(url).timeout(timeout);
|
||||
|
||||
// 提取并设置token
|
||||
String token = extractToken(filters);
|
||||
if (token != null && !token.isEmpty()) {
|
||||
request.header("Access-Token", token);
|
||||
}
|
||||
|
||||
// 设置请求体
|
||||
if (body != null) {
|
||||
request.body(body.toString()).contentType("application/json");
|
||||
}
|
||||
|
||||
try {
|
||||
log.debug("HTTP POST请求: URL={}, body={}", url, body);
|
||||
HttpResponse response = request.execute();
|
||||
String responseBody = response.body();
|
||||
log.debug("HTTP POST响应: status={}, body={}", response.getStatus(), responseBody);
|
||||
|
||||
if (responseBody == null || responseBody.isEmpty()) {
|
||||
return buildErrorResponse("响应体为空");
|
||||
}
|
||||
return JSONUtil.parseObj(responseBody);
|
||||
} catch (Exception e) {
|
||||
log.error("HTTP POST请求失败: URL={}, error={}", url, e.getMessage(), e);
|
||||
return buildErrorResponse(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建完整URL
|
||||
*/
|
||||
private static String buildUrl(String baseUrl, String path) {
|
||||
String url = baseUrl;
|
||||
if (!url.endsWith("/")) {
|
||||
url += "/";
|
||||
}
|
||||
if (path.startsWith("/")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
return url + path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建错误响应
|
||||
*/
|
||||
private static JSONObject buildErrorResponse(String errorMsg) {
|
||||
JSONObject error = new JSONObject();
|
||||
error.set("error", errorMsg);
|
||||
error.set("success", false);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查响应是否包含错误
|
||||
*/
|
||||
public static boolean hasError(JSONObject response) {
|
||||
if (response == null) {
|
||||
return true;
|
||||
}
|
||||
// 检查error字段
|
||||
if (response.containsKey("error")) {
|
||||
return true;
|
||||
}
|
||||
// 检查code字段(非0表示错误)
|
||||
if (response.containsKey("code")) {
|
||||
Integer code = response.getInt("code");
|
||||
return code != null && code != 0;
|
||||
}
|
||||
// 检查success字段
|
||||
if (response.containsKey("success")) {
|
||||
Boolean success = response.getBool("success");
|
||||
return success == null || !success;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误消息
|
||||
*/
|
||||
public static String getErrorMessage(JSONObject response) {
|
||||
if (response == null) {
|
||||
return "响应为空";
|
||||
}
|
||||
if (response.containsKey("error")) {
|
||||
return response.getStr("error");
|
||||
}
|
||||
if (response.containsKey("msg")) {
|
||||
return response.getStr("msg");
|
||||
}
|
||||
if (response.containsKey("message")) {
|
||||
return response.getStr("message");
|
||||
}
|
||||
if (response.containsKey("errMsg")) {
|
||||
return response.getStr("errMsg");
|
||||
}
|
||||
return "未知错误";
|
||||
}
|
||||
|
||||
/**
|
||||
* 从filters中提取token
|
||||
* 优先从token字段获取,其次从auth字段解析
|
||||
*/
|
||||
private static String extractToken(Map<String, Object> filters) {
|
||||
if (filters == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 优先使用token字段
|
||||
Object tokenObj = filters.get("token");
|
||||
if (tokenObj != null) {
|
||||
return String.valueOf(tokenObj);
|
||||
}
|
||||
|
||||
// 兼容旧的auth字段(JSON格式)
|
||||
Object authObj = filters.get("auth");
|
||||
if (authObj != null) {
|
||||
try {
|
||||
String authStr = String.valueOf(authObj);
|
||||
if (authStr.startsWith("{")) {
|
||||
JSONObject authJson = JSONUtil.parseObj(authStr);
|
||||
if (authJson.containsKey("apiKey")) {
|
||||
return authJson.getStr("apiKey");
|
||||
}
|
||||
if (authJson.containsKey("token")) {
|
||||
return authJson.getStr("token");
|
||||
}
|
||||
} else {
|
||||
// 直接是token字符串
|
||||
return authStr;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析auth字段失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建查询参数(排除元数据字段)
|
||||
*/
|
||||
private static Map<String, Object> buildQueryParams(Map<String, Object> filters) {
|
||||
Map<String, Object> params = new java.util.HashMap<>();
|
||||
if (filters == null) {
|
||||
return params;
|
||||
}
|
||||
|
||||
// 排除的元数据字段
|
||||
java.util.Set<String> excludeKeys = new java.util.HashSet<>();
|
||||
excludeKeys.add("endpoint");
|
||||
excludeKeys.add("auth");
|
||||
excludeKeys.add("token");
|
||||
excludeKeys.add("baseUrl");
|
||||
excludeKeys.add("apiKey");
|
||||
|
||||
for (Map.Entry<String, Object> entry : filters.entrySet()) {
|
||||
if (!excludeKeys.contains(entry.getKey()) && entry.getValue() != null) {
|
||||
params.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
}
|
||||
-1
@@ -1 +0,0 @@
|
||||
cn.wvp.protocol.config.WvpAutoConfiguration
|
||||
-318
@@ -1,318 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2025, NexIoT. All Rights Reserved.
|
||||
*
|
||||
* @Description: WVP设备服务测试类
|
||||
* @Author: gitee.com/NexIoT
|
||||
*
|
||||
*/
|
||||
package cn.wvp.protocol.service;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.universal.common.domain.R;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
import cn.wvp.protocol.service.impl.WvpDeviceServiceImpl;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* WVP设备服务单元测试
|
||||
*
|
||||
* <p>使用说明: 1. 修改 WVP_BASE_URL 为你的 WVP 平台地址 2. 修改 DEVICE_ID 和 CHANNEL_ID 为真实的设备与通道ID 3. 运行测试方法验证各项功能
|
||||
*/
|
||||
public class WvpDeviceServiceTest {
|
||||
|
||||
private WvpDeviceServiceImpl wvpDeviceService;
|
||||
|
||||
// ==================== 配置区域,请根据实际环境修改 ====================
|
||||
|
||||
/** WVP平台地址 */
|
||||
private static final String WVP_BASE_URL = "http://192.168.31.100:18080";
|
||||
|
||||
/** 测试设备ID(国标编号) */
|
||||
private static final String DEVICE_ID = "34020000001320000001";
|
||||
|
||||
/** 测试通道ID(国标编号) */
|
||||
private static final String CHANNEL_ID = "34020000001310000001";
|
||||
|
||||
/** WVP实例标识 */
|
||||
private static final String INSTANCE_KEY = "wvp20250115ABC";
|
||||
|
||||
/** WVP API Key(如启用鉴权则必填) */
|
||||
private static final String API_KEY =
|
||||
"eyJhbGciOiJSUzI1NiIsImtpZCI6IjNlNzk2NDZjNGRiYzQwODM4M2E5ZWVkMDlmMmI4NWFlIn0.eyJqdGkiOiJMLUI4cXBqUDdWdlU4VzFjbVU4bWpBIiwiaWF0IjoxNzYyNDI2MzYxLCJleHAiOjEwNTc0NzM0NjkxNDgwOSwibmJmIjoxNzYyNDI2MzYxLCJzdWIiOiJsb2dpbiIsImF1ZCI6IkF1ZGllbmNlIiwidXNlck5hbWUiOiJhZG1pbiIsImFwaUtleUlkIjoxfQ.YK1dbexi66p8dbztPsTWpG2r5pb8gGebH59A-tYZKPqrLstLC6KhiYtPBoB_KItc-OBdlEQDpL5mZAtYgYY-ogS4JwTpOzgvOUh8mbEa68tdiughRrSohO5O43kUF67iwmEk9XalA0OpuOLTZPFcElApAiwUULtWz21AYOU17kFtkQ4AXvugDpOgRDmhHVpAvxW7bMbq70BroieO2iYNVtfCNS-qEWmA7zG0nDiQY3kk4mX7ftVg5IrCnS8xD5vg0GBq3mib06UaagWUFJ10I8vIe5smAQOe2s96aKuT88y_Ok43xVdsAkCS9pO9qjFKRz-aXHnb6zZmMBwaL3KcSQ"; // 请从WVP后台获取
|
||||
|
||||
// ====================================================================
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
wvpDeviceService = new WvpDeviceServiceImpl();
|
||||
System.out.println("=".repeat(80));
|
||||
System.out.println("WVP平台地址: " + WVP_BASE_URL);
|
||||
System.out.println("测试设备ID: " + DEVICE_ID);
|
||||
System.out.println("测试通道ID: " + CHANNEL_ID);
|
||||
System.out.println("=".repeat(80));
|
||||
}
|
||||
|
||||
/** 创建WVP下行请求对象 */
|
||||
private WvpDownRequest createWvpRequest(String deviceId, JSONObject data) {
|
||||
WvpDownRequest request = new WvpDownRequest();
|
||||
request.setDeviceId(deviceId);
|
||||
request.setData(data);
|
||||
|
||||
Map<String, Object> wvpRequestData = new HashMap<>();
|
||||
wvpRequestData.put("endpoint", WVP_BASE_URL);
|
||||
// 添加API Key鉴权
|
||||
if (API_KEY != null && !API_KEY.equals("your_api_key_here")) {
|
||||
wvpRequestData.put("token", API_KEY);
|
||||
}
|
||||
request.setWvpRequestData(wvpRequestData);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
/** 打印测试结果 */
|
||||
private void printResult(String testName, R<?> result) {
|
||||
System.out.println("\n【" + testName + "】");
|
||||
System.out.println("成功: " + result.isSuccess());
|
||||
System.out.println("消息: " + result.getMsg());
|
||||
if (result.getData() != null) {
|
||||
System.out.println("数据: " + JSONUtil.toJsonPrettyStr(result.getData()));
|
||||
}
|
||||
System.out.println("-".repeat(80));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListDevices() {
|
||||
System.out.println("\n>>> 测试设备列表查询");
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
filters.put("endpoint", WVP_BASE_URL);
|
||||
filters.put("page", 1);
|
||||
filters.put("count", 10);
|
||||
JSONObject set = new JSONObject();
|
||||
set.put("apiKey", API_KEY);
|
||||
Map<String, Object> auth = new HashMap<>();
|
||||
filters.put("auth", set);
|
||||
R<?> result = wvpDeviceService.listDevices(INSTANCE_KEY, filters);
|
||||
printResult("设备列表查询", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListChannels() {
|
||||
System.out.println("\n>>> 测试通道列表查询");
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
filters.put("endpoint", WVP_BASE_URL);
|
||||
filters.put("page", 1); // 当前页
|
||||
filters.put("count", 20); // 每页查询数量
|
||||
// filters.put("query", null);
|
||||
// filters.put("online", true);
|
||||
filters.put("channelType", 0); // 0:国标设备,1:推流设备,2:拉流代理"
|
||||
// filters.put("groupDeviceId", null);// 0:国标设备,1:推流设备,2:拉流代理"
|
||||
JSONObject set = new JSONObject();
|
||||
set.put("apiKey", API_KEY);
|
||||
Map<String, Object> auth = new HashMap<>();
|
||||
filters.put("auth", set);
|
||||
R<?> result = wvpDeviceService.listChannels(DEVICE_ID, filters);
|
||||
printResult("通道列表查询", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartPreview() {
|
||||
System.out.println("\n>>> 测试直播开始");
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.put("endpoint", WVP_BASE_URL);
|
||||
data.set("channelId", CHANNEL_ID);
|
||||
data.set("protocol", "flv"); // 优先协议
|
||||
data.set("token", API_KEY);
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.startPreview(request);
|
||||
printResult("直播开始", result);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
System.out.println("\n播放地址已获取,可用于 jessibuca 播放器:");
|
||||
JSONObject resultData = JSONUtil.parseObj(result.getData());
|
||||
JSONObject urls = resultData.getJSONObject("urls");
|
||||
if (urls != null) {
|
||||
System.out.println("FLV: " + urls.getStr("flv"));
|
||||
System.out.println("HLS: " + urls.getStr("hls"));
|
||||
System.out.println("优先协议: " + resultData.getStr("prefer"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStopPreview() {
|
||||
System.out.println("\n>>> 测试直播停止");
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("channelId", CHANNEL_ID);
|
||||
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.stopPreview(request);
|
||||
printResult("直播停止", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPtzControl() {
|
||||
System.out.println("\n>>> 测试云台控制");
|
||||
|
||||
// 测试向左转
|
||||
JSONObject dataLeft = new JSONObject();
|
||||
dataLeft.set("channelId", CHANNEL_ID);
|
||||
dataLeft.set("command", "left");
|
||||
dataLeft.set("speed", 50);
|
||||
|
||||
WvpDownRequest requestLeft = createWvpRequest(DEVICE_ID, dataLeft);
|
||||
R<?> resultLeft = wvpDeviceService.ptzControl(requestLeft);
|
||||
printResult("云台控制-左转", resultLeft);
|
||||
|
||||
// 延时
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
|
||||
// 测试停止
|
||||
JSONObject dataStop = new JSONObject();
|
||||
dataStop.set("channelId", CHANNEL_ID);
|
||||
dataStop.set("command", "stop");
|
||||
dataStop.set("speed", 0);
|
||||
|
||||
WvpDownRequest requestStop = createWvpRequest(DEVICE_ID, dataStop);
|
||||
R<?> resultStop = wvpDeviceService.ptzControl(requestStop);
|
||||
printResult("云台控制-停止", resultStop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryPresets() {
|
||||
System.out.println("\n>>> 测试预置位查询");
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("channelId", CHANNEL_ID);
|
||||
data.set("timeout", 15);
|
||||
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.queryPresets(request);
|
||||
printResult("预置位查询", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSnapshot() {
|
||||
System.out.println("\n>>> 测试抓拍");
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("channelId", CHANNEL_ID);
|
||||
data.set("mark", "abc" + RandomUtil.randomString(3));
|
||||
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.snapshot(request);
|
||||
printResult("抓拍", result);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
JSONObject resultData = JSONUtil.parseObj(result.getData());
|
||||
System.out.println("\n抓拍图片URL: " + resultData.getStr("snapshotUrl"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartPlayback() {
|
||||
System.out.println("\n>>> 测试历史回放");
|
||||
|
||||
// 查询最近1小时的录像
|
||||
long endTime = System.currentTimeMillis() / 1000;
|
||||
long startTime = endTime - 3600; // 1小时前
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("channelId", CHANNEL_ID);
|
||||
data.set("startTime", startTime);
|
||||
data.set("endTime", endTime);
|
||||
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.startPlayback(request);
|
||||
printResult("历史回放", result);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
System.out.println("\n回放地址已获取:");
|
||||
JSONObject resultData = JSONUtil.parseObj(result.getData());
|
||||
JSONObject urls = resultData.getJSONObject("urls");
|
||||
if (urls != null) {
|
||||
System.out.println("FLV: " + urls.getStr("flv"));
|
||||
System.out.println("HLS: " + urls.getStr("hls"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 完整流程测试:设备列表 -> 通道列表 -> 直播 -> 云台 -> 停止 */
|
||||
@Test
|
||||
public void testCompleteFlow() {
|
||||
System.out.println("\n========== 完整流程测试 ==========");
|
||||
|
||||
// 1. 查询设备列表
|
||||
System.out.println("\n步骤1: 查询设备列表");
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
filters.put("endpoint", WVP_BASE_URL);
|
||||
filters.put("limit", 5);
|
||||
R<?> devicesResult = wvpDeviceService.listDevices(INSTANCE_KEY, filters);
|
||||
printResult("设备列表", devicesResult);
|
||||
|
||||
// 2. 查询通道列表
|
||||
System.out.println("\n步骤2: 查询通道列表");
|
||||
Map<String, Object> channelFilters = new HashMap<>();
|
||||
channelFilters.put("endpoint", WVP_BASE_URL);
|
||||
channelFilters.put("limit", 5);
|
||||
R<?> channelsResult = wvpDeviceService.listChannels(DEVICE_ID, channelFilters);
|
||||
printResult("通道列表", channelsResult);
|
||||
|
||||
// 3. 开始直播
|
||||
System.out.println("\n步骤3: 开始直播");
|
||||
JSONObject previewData = new JSONObject();
|
||||
previewData.set("channelId", CHANNEL_ID);
|
||||
previewData.set("protocol", "flv");
|
||||
WvpDownRequest previewRequest = createWvpRequest(DEVICE_ID, previewData);
|
||||
R<?> previewResult = wvpDeviceService.startPreview(previewRequest);
|
||||
printResult("开始直播", previewResult);
|
||||
|
||||
if (previewResult.isSuccess()) {
|
||||
// 4. 云台控制
|
||||
System.out.println("\n步骤4: 云台控制(向右转2秒)");
|
||||
JSONObject ptzData = new JSONObject();
|
||||
ptzData.set("channelId", CHANNEL_ID);
|
||||
ptzData.set("command", "right");
|
||||
ptzData.set("speed", 50);
|
||||
WvpDownRequest ptzRequest = createWvpRequest(DEVICE_ID, ptzData);
|
||||
R<?> ptzResult = wvpDeviceService.ptzControl(ptzRequest);
|
||||
printResult("云台控制", ptzResult);
|
||||
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
|
||||
// 5. 停止云台
|
||||
System.out.println("\n步骤5: 停止云台");
|
||||
JSONObject stopPtzData = new JSONObject();
|
||||
stopPtzData.set("channelId", CHANNEL_ID);
|
||||
stopPtzData.set("command", "stop");
|
||||
stopPtzData.set("speed", 0);
|
||||
WvpDownRequest stopPtzRequest = createWvpRequest(DEVICE_ID, stopPtzData);
|
||||
wvpDeviceService.ptzControl(stopPtzRequest);
|
||||
|
||||
// 6. 停止直播
|
||||
System.out.println("\n步骤6: 停止直播");
|
||||
JSONObject stopData = new JSONObject();
|
||||
stopData.set("channelId", CHANNEL_ID);
|
||||
WvpDownRequest stopRequest = createWvpRequest(DEVICE_ID, stopData);
|
||||
R<?> stopResult = wvpDeviceService.stopPreview(stopRequest);
|
||||
printResult("停止直播", stopResult);
|
||||
}
|
||||
|
||||
System.out.println("\n========== 流程测试完成 ==========");
|
||||
}
|
||||
}
|
||||
-605
@@ -1,605 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (c) 2025, NexIoT. All Rights Reserved.
|
||||
*
|
||||
* @Description: WVP设备服务测试类
|
||||
* @Author: gitee.com/NexIoT
|
||||
*
|
||||
*/
|
||||
package cn.wvp.protocol.service;
|
||||
|
||||
import cn.hutool.core.date.DateField;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import cn.universal.common.domain.R;
|
||||
import cn.wvp.protocol.entity.WvpDownRequest;
|
||||
import cn.wvp.protocol.service.impl.WvpDeviceServiceImpl;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* WVP设备服务单元测试
|
||||
*
|
||||
* <p>使用说明: 1. 修改 WVP_BASE_URL 为你的 WVP 平台地址 2. 修改 DEVICE_ID 和 CHANNEL_ID 为真实的设备与通道ID 3. 运行测试方法验证各项功能
|
||||
*/
|
||||
public class WvpDeviceServiceTest2 {
|
||||
|
||||
private WvpDeviceServiceImpl wvpDeviceService;
|
||||
|
||||
// ==================== 配置区域,请根据实际环境修改 ====================
|
||||
|
||||
/** WVP平台地址 */
|
||||
private static final String WVP_BASE_URL = "http://192.168.31.100:18080";
|
||||
|
||||
/** 测试设备ID(国标编号) */
|
||||
private static final String DEVICE_ID = "34020000001320000001";
|
||||
|
||||
/** 测试通道ID(国标编号) */
|
||||
private static final String CHANNEL_ID = "34020000001310000001";
|
||||
|
||||
/** WVP实例标识 */
|
||||
private static final String INSTANCE_KEY = "wvp20250115ABC";
|
||||
|
||||
/** WVP API Key(如启用鉴权则必填) */
|
||||
private static final String API_KEY =
|
||||
"eyJhbGciOiJSUzI1NiIsImtpZCI6IjNlNzk2NDZjNGRiYzQwODM4M2E5ZWVkMDlmMmI4NWFlIn0.eyJqdGkiOiJMLUI4cXBqUDdWdlU4VzFjbVU4bWpBIiwiaWF0IjoxNzYyNDI2MzYxLCJleHAiOjEwNTc0NzM0NjkxNDgwOSwibmJmIjoxNzYyNDI2MzYxLCJzdWIiOiJsb2dpbiIsImF1ZCI6IkF1ZGllbmNlIiwidXNlck5hbWUiOiJhZG1pbiIsImFwaUtleUlkIjoxfQ.YK1dbexi66p8dbztPsTWpG2r5pb8gGebH59A-tYZKPqrLstLC6KhiYtPBoB_KItc-OBdlEQDpL5mZAtYgYY-ogS4JwTpOzgvOUh8mbEa68tdiughRrSohO5O43kUF67iwmEk9XalA0OpuOLTZPFcElApAiwUULtWz21AYOU17kFtkQ4AXvugDpOgRDmhHVpAvxW7bMbq70BroieO2iYNVtfCNS-qEWmA7zG0nDiQY3kk4mX7ftVg5IrCnS8xD5vg0GBq3mib06UaagWUFJ10I8vIe5smAQOe2s96aKuT88y_Ok43xVdsAkCS9pO9qjFKRz-aXHnb6zZmMBwaL3KcSQ"; // 请从WVP后台获取
|
||||
|
||||
// ====================================================================
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
wvpDeviceService = new WvpDeviceServiceImpl();
|
||||
System.out.println("=".repeat(80));
|
||||
System.out.println("WVP平台地址: " + WVP_BASE_URL);
|
||||
System.out.println("测试设备ID: " + DEVICE_ID);
|
||||
System.out.println("测试通道ID: " + CHANNEL_ID);
|
||||
System.out.println("=".repeat(80));
|
||||
}
|
||||
|
||||
/** 创建WVP下行请求对象 */
|
||||
private WvpDownRequest createWvpRequest(String deviceId, JSONObject data) {
|
||||
WvpDownRequest request = new WvpDownRequest();
|
||||
request.setDeviceId(deviceId);
|
||||
request.setData(data);
|
||||
|
||||
Map<String, Object> wvpRequestData = new HashMap<>();
|
||||
wvpRequestData.put("endpoint", WVP_BASE_URL);
|
||||
// 添加token鉴权
|
||||
if (API_KEY != null && !API_KEY.isEmpty()) {
|
||||
wvpRequestData.put("token", API_KEY);
|
||||
}
|
||||
request.setWvpRequestData(wvpRequestData);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
/** 打印测试结果 */
|
||||
private void printResult(String testName, R<?> result) {
|
||||
System.out.println("\n【" + testName + "】");
|
||||
System.out.println("成功: " + result.isSuccess());
|
||||
System.out.println("消息: " + result.getMsg());
|
||||
if (result.getData() != null) {
|
||||
System.out.println("数据: " + JSONUtil.toJsonPrettyStr(result.getData()));
|
||||
}
|
||||
System.out.println("-".repeat(80));
|
||||
}
|
||||
|
||||
/** 打印测试结果 */
|
||||
private void printResult(String testName, String body) {
|
||||
System.out.println("\n【" + testName + "】");
|
||||
System.out.println("成功: " + body);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListDevices() {
|
||||
System.out.println("\n>>> 测试设备列表查询");
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
filters.put("endpoint", WVP_BASE_URL);
|
||||
filters.put("token", API_KEY);
|
||||
filters.put("page", 1);
|
||||
filters.put("count", 10);
|
||||
|
||||
R<?> result = wvpDeviceService.listDevices(INSTANCE_KEY, filters);
|
||||
printResult("设备列表查询", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListOrgs() {
|
||||
System.out.println("\n>>> 测试组织列表查询");
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
filters.put("endpoint", WVP_BASE_URL);
|
||||
filters.put("token", API_KEY);
|
||||
|
||||
R<?> result = wvpDeviceService.listOrgs(INSTANCE_KEY, filters);
|
||||
printResult("组织列表查询", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListChannels() {
|
||||
System.out.println("\n>>> 测试通道列表查询");
|
||||
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
filters.put("endpoint", WVP_BASE_URL);
|
||||
filters.put("token", API_KEY);
|
||||
filters.put("page", 1);
|
||||
filters.put("count", 20);
|
||||
|
||||
R<?> result = wvpDeviceService.listChannels(DEVICE_ID, filters);
|
||||
printResult("通道列表查询", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartPreview() {
|
||||
System.out.println("\n>>> 测试直播开始");
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("channelId", CHANNEL_ID);
|
||||
data.set("protocol", "flv"); // 优先协议
|
||||
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.startPreview(request);
|
||||
printResult("直播开始", result);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
System.out.println("\n播放地址已获取,可用于 jessibuca 播放器:");
|
||||
JSONObject resultData = JSONUtil.parseObj(result.getData());
|
||||
JSONObject urls = resultData.getJSONObject("urls");
|
||||
if (urls != null) {
|
||||
System.out.println("FLV: " + urls.getStr("flv"));
|
||||
System.out.println("HLS: " + urls.getStr("hls"));
|
||||
System.out.println("优先协议: " + resultData.getStr("prefer"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStopPreview() {
|
||||
System.out.println("\n>>> 测试直播停止");
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("channelId", CHANNEL_ID);
|
||||
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.stopPreview(request);
|
||||
printResult("直播停止", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPtzControl() {
|
||||
System.out.println("\n>>> 测试云台控制");
|
||||
|
||||
// 测试向左转
|
||||
JSONObject dataLeft = new JSONObject();
|
||||
dataLeft.set("channelId", CHANNEL_ID);
|
||||
dataLeft.set("command", "left");
|
||||
dataLeft.set("speed", 50);
|
||||
|
||||
WvpDownRequest requestLeft = createWvpRequest(DEVICE_ID, dataLeft);
|
||||
R<?> resultLeft = wvpDeviceService.ptzControl(requestLeft);
|
||||
printResult("云台控制-左转", resultLeft);
|
||||
|
||||
// 延时
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
|
||||
// 测试停止
|
||||
JSONObject dataStop = new JSONObject();
|
||||
dataStop.set("channelId", CHANNEL_ID);
|
||||
dataStop.set("command", "stop");
|
||||
dataStop.set("speed", 0);
|
||||
|
||||
WvpDownRequest requestStop = createWvpRequest(DEVICE_ID, dataStop);
|
||||
R<?> resultStop = wvpDeviceService.ptzControl(requestStop);
|
||||
printResult("云台控制-停止", resultStop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryPresets() {
|
||||
System.out.println("\n>>> 测试预置位查询");
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("channelId", CHANNEL_ID);
|
||||
data.set("timeout", 15);
|
||||
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.queryPresets(request);
|
||||
printResult("预置位查询", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSnapshot() {
|
||||
System.out.println("\n>>> 测试抓拍");
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("channelId", CHANNEL_ID);
|
||||
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.snapshot(request);
|
||||
printResult("抓拍", result);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
JSONObject resultData = JSONUtil.parseObj(result.getData());
|
||||
System.out.println("\n抓拍图片URL: " + resultData.getStr("snapshotUrl"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartPlayback() {
|
||||
System.out.println("\n>>> 测试历史回放");
|
||||
|
||||
// 查询最近1小时的录像
|
||||
long endTime = System.currentTimeMillis() / 1000;
|
||||
long startTime = endTime - 13600; // 1小时前
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("channelId", CHANNEL_ID);
|
||||
data.set("startTime", startTime);
|
||||
data.set("endTime", endTime);
|
||||
|
||||
|
||||
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.startPlayback(request);
|
||||
printResult("历史回放", result);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
System.out.println("\n回放地址已获取:");
|
||||
JSONObject resultData = JSONUtil.parseObj(result.getData());
|
||||
JSONObject urls = resultData.getJSONObject("urls");
|
||||
if (urls != null) {
|
||||
System.out.println("FLV: " + urls.getStr("flv"));
|
||||
System.out.println("HLS: " + urls.getStr("hls"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStopPlayback() {
|
||||
System.out.println("\n>>> 测试停止回放");
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("channelId", CHANNEL_ID);
|
||||
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.stopPlayback(request);
|
||||
printResult("停止回放", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryGBRecords() {
|
||||
System.out.println("\n>>> 测试国标录像查询");
|
||||
|
||||
// 查询最近24小时的国标录像
|
||||
long endTime = System.currentTimeMillis() / 1000;
|
||||
long startTime = endTime - 86400; // 24小时前
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("channelId", CHANNEL_ID);
|
||||
data.set("startTime",DateUtil.formatDateTime(DateUtil.offset(new Date(), DateField.HOUR,-78)));
|
||||
data.set("endTime", DateUtil.now());
|
||||
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.queryGBRecords(request);
|
||||
printResult("国标录像查询", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartGBRecordDownload() {
|
||||
System.out.println("\n>>> 测试开始下载国标录像");
|
||||
|
||||
// 查询最近1小时的录像
|
||||
long endTime = System.currentTimeMillis() / 1000;
|
||||
long startTime = endTime - 3600; // 1小时前
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("channelId", CHANNEL_ID);
|
||||
data.set("startTime", startTime);
|
||||
data.set("endTime", endTime);
|
||||
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.startGBRecordDownload(request);
|
||||
printResult("开始下载国标录像", result);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
JSONObject resultData = JSONUtil.parseObj(result.getData());
|
||||
String downloadId = resultData.getStr("downloadId");
|
||||
System.out.println("\n下载ID: " + downloadId);
|
||||
System.out.println("可使用此ID查询下载进度或停止下载");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStopGBRecordDownload() {
|
||||
System.out.println("\n>>> 测试停止下载国标录像");
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("channelId", CHANNEL_ID);
|
||||
data.set("stream", "your_stream_id"); // 需要从开始下载接口获取
|
||||
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.stopGBRecordDownload(request);
|
||||
printResult("停止下载国标录像", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetGBRecordDownloadProgress() {
|
||||
System.out.println("\n>>> 测试查询国标录像下载进度");
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("channelId", CHANNEL_ID);
|
||||
data.set("stream", "your_stream_id"); // 需要从开始下载接口获取
|
||||
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.getGBRecordDownloadProgress(request);
|
||||
printResult("查询国标录像下载进度", result);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
JSONObject resultData = JSONUtil.parseObj(result.getData());
|
||||
System.out.println("\n下载进度: " + resultData.getInt("progress") + "%");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryCloudRecordDates() {
|
||||
System.out.println("\n>>> 测试查询云端录像日期列表");
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("app", "rtp");
|
||||
data.set("stream", DEVICE_ID + "_" + CHANNEL_ID);
|
||||
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.queryCloudRecordDates(request);
|
||||
printResult("查询云端录像日期列表", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryCloudRecords() {
|
||||
System.out.println("\n>>> 测试查询云端录像列表");
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("app", "rtp");
|
||||
data.set("stream", DEVICE_ID + "_" + CHANNEL_ID);
|
||||
data.set("page", 1);
|
||||
data.set("count", 20);
|
||||
data.set("startTime", "2025-11-01 00:00:00");
|
||||
data.set("endTime", "2025-11-31 23:59:59");
|
||||
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.queryCloudRecords(request);
|
||||
printResult("查询云端录像列表", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadCloudRecord() {
|
||||
System.out.println("\n>>> 测试加载云端录像文件");
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("app", "rtp");
|
||||
data.set("stream", DEVICE_ID + "_" + CHANNEL_ID);
|
||||
data.set("cloudRecordId", 64);
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.loadCloudRecord(request);
|
||||
printResult("加载云端录像文件", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSeekCloudRecord() {
|
||||
System.out.println("\n>>> 测试定位云端录像进度");
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("app", "rtp");
|
||||
data.set("stream", DEVICE_ID + "_" + CHANNEL_ID);
|
||||
data.set("seek", 60000); // 定位到60秒处
|
||||
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.seekCloudRecord(request);
|
||||
printResult("定位云端录像进度", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetCloudRecordSpeed() {
|
||||
System.out.println("\n>>> 测试设置云端录像倍速");
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
data.set("app", "rtp");
|
||||
data.set("stream", DEVICE_ID + "_" + CHANNEL_ID);
|
||||
data.set("speed", 2.0); // 2倍速播放
|
||||
|
||||
WvpDownRequest request = createWvpRequest(DEVICE_ID, data);
|
||||
R<?> result = wvpDeviceService.setCloudRecordSpeed(request);
|
||||
printResult("设置云端录像倍速", result);
|
||||
}
|
||||
|
||||
/** 完整流程测试:设备列表 -> 通道列表 -> 直播 -> 云台 -> 停止 */
|
||||
@Test
|
||||
public void testCompleteFlow() {
|
||||
System.out.println("\n========== 完整流程测试 ==========");
|
||||
|
||||
// 1. 查询设备列表
|
||||
System.out.println("\n步骤1: 查询设备列表");
|
||||
Map<String, Object> filters = new HashMap<>();
|
||||
filters.put("endpoint", WVP_BASE_URL);
|
||||
filters.put("token", API_KEY);
|
||||
filters.put("page", 1);
|
||||
filters.put("count", 5);
|
||||
R<?> devicesResult = wvpDeviceService.listDevices(INSTANCE_KEY, filters);
|
||||
printResult("设备列表", devicesResult);
|
||||
|
||||
// 2. 查询通道列表
|
||||
System.out.println("\n步骤2: 查询通道列表");
|
||||
Map<String, Object> channelFilters = new HashMap<>();
|
||||
channelFilters.put("endpoint", WVP_BASE_URL);
|
||||
channelFilters.put("token", API_KEY);
|
||||
channelFilters.put("page", 1);
|
||||
channelFilters.put("count", 5);
|
||||
R<?> channelsResult = wvpDeviceService.listChannels(DEVICE_ID, channelFilters);
|
||||
printResult("通道列表", channelsResult);
|
||||
|
||||
// 3. 开始直播
|
||||
System.out.println("\n步骤3: 开始直播");
|
||||
JSONObject previewData = new JSONObject();
|
||||
previewData.set("channelId", CHANNEL_ID);
|
||||
previewData.set("protocol", "flv");
|
||||
WvpDownRequest previewRequest = createWvpRequest(DEVICE_ID, previewData);
|
||||
R<?> previewResult = wvpDeviceService.startPreview(previewRequest);
|
||||
printResult("开始直播", previewResult);
|
||||
|
||||
if (previewResult.isSuccess()) {
|
||||
// 4. 云台控制
|
||||
System.out.println("\n步骤4: 云台控制(向右转2秒)");
|
||||
JSONObject ptzData = new JSONObject();
|
||||
ptzData.set("channelId", CHANNEL_ID);
|
||||
ptzData.set("command", "right");
|
||||
ptzData.set("speed", 50);
|
||||
WvpDownRequest ptzRequest = createWvpRequest(DEVICE_ID, ptzData);
|
||||
R<?> ptzResult = wvpDeviceService.ptzControl(ptzRequest);
|
||||
printResult("云台控制", ptzResult);
|
||||
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
|
||||
// 5. 停止云台
|
||||
System.out.println("\n步骤5: 停止云台");
|
||||
JSONObject stopPtzData = new JSONObject();
|
||||
stopPtzData.set("channelId", CHANNEL_ID);
|
||||
stopPtzData.set("command", "stop");
|
||||
stopPtzData.set("speed", 0);
|
||||
WvpDownRequest stopPtzRequest = createWvpRequest(DEVICE_ID, stopPtzData);
|
||||
wvpDeviceService.ptzControl(stopPtzRequest);
|
||||
|
||||
// 6. 停止直播
|
||||
System.out.println("\n步骤6: 停止直播");
|
||||
JSONObject stopData = new JSONObject();
|
||||
stopData.set("channelId", CHANNEL_ID);
|
||||
WvpDownRequest stopRequest = createWvpRequest(DEVICE_ID, stopData);
|
||||
R<?> stopResult = wvpDeviceService.stopPreview(stopRequest);
|
||||
printResult("停止直播", stopResult);
|
||||
}
|
||||
|
||||
System.out.println("\n========== 流程测试完成 ==========");
|
||||
}
|
||||
|
||||
/** 完整录像流程测试:国标录像查询 -> 录像回放 -> 停止回放 */
|
||||
@Test
|
||||
public void testRecordCompleteFlow() {
|
||||
System.out.println("\n========== 完整录像流程测试 ==========");
|
||||
|
||||
long endTime = System.currentTimeMillis() / 1000;
|
||||
long startTime = endTime - 86400; // 24小时前
|
||||
|
||||
// 1. 查询国标录像
|
||||
System.out.println("\n步骤1: 查询国标录像");
|
||||
JSONObject queryData = new JSONObject();
|
||||
queryData.set("channelId", CHANNEL_ID);
|
||||
queryData.set("startTime", startTime);
|
||||
queryData.set("endTime", endTime);
|
||||
WvpDownRequest queryRequest = createWvpRequest(DEVICE_ID, queryData);
|
||||
R<?> queryResult = wvpDeviceService.queryGBRecords(queryRequest);
|
||||
printResult("国标录像查询", queryResult);
|
||||
|
||||
// 2. 开始录像回放
|
||||
System.out.println("\n步骤2: 开始录像回放");
|
||||
JSONObject playbackData = new JSONObject();
|
||||
playbackData.set("channelId", CHANNEL_ID);
|
||||
playbackData.set("startTime", startTime);
|
||||
playbackData.set("endTime", endTime);
|
||||
WvpDownRequest playbackRequest = createWvpRequest(DEVICE_ID, playbackData);
|
||||
R<?> playbackResult = wvpDeviceService.startPlayback(playbackRequest);
|
||||
printResult("开始录像回放", playbackResult);
|
||||
|
||||
if (playbackResult.isSuccess()) {
|
||||
// 3. 等待5秒
|
||||
try {
|
||||
System.out.println("\n等待5秒...");
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
|
||||
// 4. 停止回放
|
||||
System.out.println("\n步骤3: 停止录像回放");
|
||||
JSONObject stopData = new JSONObject();
|
||||
stopData.set("channelId", CHANNEL_ID);
|
||||
WvpDownRequest stopRequest = createWvpRequest(DEVICE_ID, stopData);
|
||||
R<?> stopResult = wvpDeviceService.stopPlayback(stopRequest);
|
||||
printResult("停止录像回放", stopResult);
|
||||
}
|
||||
|
||||
System.out.println("\n========== 录像流程测试完成 ==========");
|
||||
}
|
||||
|
||||
/** 云端录像完整流程测试 */
|
||||
@Test
|
||||
public void testCloudRecordCompleteFlow() {
|
||||
System.out.println("\n========== 云端录像完整流程测试 ==========");
|
||||
|
||||
String app = "rtp";
|
||||
String stream = DEVICE_ID + "_" + CHANNEL_ID;
|
||||
|
||||
// 1. 查询云端录像日期列表
|
||||
System.out.println("\n步骤1: 查询云端录像日期列表");
|
||||
JSONObject dateData = new JSONObject();
|
||||
dateData.set("app", app);
|
||||
dateData.set("stream", stream);
|
||||
WvpDownRequest dateRequest = createWvpRequest(DEVICE_ID, dateData);
|
||||
R<?> dateResult = wvpDeviceService.queryCloudRecordDates(dateRequest);
|
||||
printResult("云端录像日期列表", dateResult);
|
||||
|
||||
// 2. 查询云端录像列表
|
||||
System.out.println("\n步骤2: 查询云端录像列表");
|
||||
JSONObject listData = new JSONObject();
|
||||
listData.set("app", app);
|
||||
listData.set("stream", stream);
|
||||
listData.set("page", 1);
|
||||
listData.set("count", 20);
|
||||
WvpDownRequest listRequest = createWvpRequest(DEVICE_ID, listData);
|
||||
R<?> listResult = wvpDeviceService.queryCloudRecords(listRequest);
|
||||
printResult("云端录像列表", listResult);
|
||||
|
||||
// 3. 加载云端录像文件
|
||||
System.out.println("\n步骤3: 加载云端录像文件");
|
||||
long endTime = System.currentTimeMillis();
|
||||
long startTime = endTime - 3600000; // 1小时前
|
||||
JSONObject loadData = new JSONObject();
|
||||
loadData.set("app", app);
|
||||
loadData.set("stream", stream);
|
||||
loadData.set("startTime", startTime);
|
||||
loadData.set("endTime", endTime);
|
||||
WvpDownRequest loadRequest = createWvpRequest(DEVICE_ID, loadData);
|
||||
R<?> loadResult = wvpDeviceService.loadCloudRecord(loadRequest);
|
||||
printResult("加载云端录像文件", loadResult);
|
||||
|
||||
if (loadResult.isSuccess()) {
|
||||
// 4. 设置倍速播放
|
||||
System.out.println("\n步骤4: 设置2倍速播放");
|
||||
JSONObject speedData = new JSONObject();
|
||||
speedData.set("app", app);
|
||||
speedData.set("stream", stream);
|
||||
speedData.set("speed", 2.0);
|
||||
WvpDownRequest speedRequest = createWvpRequest(DEVICE_ID, speedData);
|
||||
R<?> speedResult = wvpDeviceService.setCloudRecordSpeed(speedRequest);
|
||||
printResult("设置倍速播放", speedResult);
|
||||
|
||||
// 5. 定位录像进度
|
||||
System.out.println("\n步骤5: 定位到60秒处");
|
||||
JSONObject seekData = new JSONObject();
|
||||
seekData.set("app", app);
|
||||
seekData.set("stream", stream);
|
||||
seekData.set("seek", 60000); // 60秒
|
||||
WvpDownRequest seekRequest = createWvpRequest(DEVICE_ID, seekData);
|
||||
R<?> seekResult = wvpDeviceService.seekCloudRecord(seekRequest);
|
||||
printResult("定位录像进度", seekResult);
|
||||
}
|
||||
|
||||
System.out.println("\n========== 云端录像流程测试完成 ==========");
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
{
|
||||
"tags": [],
|
||||
"events": [
|
||||
{
|
||||
"id": "online",
|
||||
"name": "设备上线",
|
||||
"valueType": {"type": "string"}
|
||||
},
|
||||
{
|
||||
"id": "offline",
|
||||
"name": "设备下线",
|
||||
"valueType": {"type": "string"}
|
||||
}
|
||||
],
|
||||
"functions": [
|
||||
{
|
||||
"id": "cameraLiveStream",
|
||||
"name": "实时预览",
|
||||
"config": false,
|
||||
"inputs": [
|
||||
{"id": "deviceId", "name": "设备ID", "valueType": {"type": "string"}},
|
||||
{"id": "channelId", "name": "通道ID", "valueType": {"type": "string"}},
|
||||
{"id": "streamType", "name": "码流类型", "valueType": {"type": "enum", "elements": [{"text": "主码流", "value": "main"}, {"text": "子码流", "value": "sub"}]}}
|
||||
],
|
||||
"output": {}
|
||||
},
|
||||
{
|
||||
"id": "cameraTurn",
|
||||
"name": "云台控制",
|
||||
"config": false,
|
||||
"inputs": [
|
||||
{"id": "deviceId", "name": "设备ID", "valueType": {"type": "string"}},
|
||||
{"id": "channelId", "name": "通道ID", "valueType": {"type": "string"}},
|
||||
{"id": "command", "name": "控制命令", "valueType": {"type": "string"}},
|
||||
{"id": "speed", "name": "速度", "valueType": {"type": "int"}}
|
||||
],
|
||||
"output": {}
|
||||
},
|
||||
{
|
||||
"id": "cameraPlayback",
|
||||
"name": "录像回放",
|
||||
"config": false,
|
||||
"inputs": [
|
||||
{"id": "deviceId", "name": "设备ID", "valueType": {"type": "string"}},
|
||||
{"id": "channelId", "name": "通道ID", "valueType": {"type": "string"}},
|
||||
{"id": "startTime", "name": "开始时间", "valueType": {"type": "long"}},
|
||||
{"id": "endTime", "name": "结束时间", "valueType": {"type": "long"}}
|
||||
],
|
||||
"output": {}
|
||||
},
|
||||
{
|
||||
"id": "cameraSnapshot",
|
||||
"name": "抓拍",
|
||||
"config": false,
|
||||
"inputs": [
|
||||
{"id": "deviceId", "name": "设备ID", "valueType": {"type": "string"}},
|
||||
{"id": "channelId", "name": "通道ID", "valueType": {"type": "string"}}
|
||||
],
|
||||
"output": {}
|
||||
},
|
||||
{
|
||||
"id": "queryRecords",
|
||||
"name": "查询录像",
|
||||
"config": false,
|
||||
"inputs": [
|
||||
{"id": "deviceId", "name": "设备ID", "valueType": {"type": "string"}},
|
||||
{"id": "channelId", "name": "通道ID", "valueType": {"type": "string"}},
|
||||
{"id": "startTime", "name": "开始时间", "valueType": {"type": "string"}},
|
||||
{"id": "endTime", "name": "结束时间", "valueType": {"type": "string"}}
|
||||
],
|
||||
"output": {}
|
||||
},
|
||||
{
|
||||
"id": "queryCloudRecords",
|
||||
"name": "查询云端录像",
|
||||
"config": false,
|
||||
"inputs": [
|
||||
{"id": "app", "name": "应用名", "valueType": {"type": "string"}},
|
||||
{"id": "stream", "name": "流ID", "valueType": {"type": "string"}},
|
||||
{"id": "page", "name": "页码", "valueType": {"type": "int"}},
|
||||
{"id": "count", "name": "每页数量", "valueType": {"type": "int"}}
|
||||
],
|
||||
"output": {}
|
||||
},
|
||||
{
|
||||
"id": "loadCloudRecord",
|
||||
"name": "加载云端录像",
|
||||
"config": false,
|
||||
"inputs": [
|
||||
{"id": "app", "name": "应用名", "valueType": {"type": "string"}},
|
||||
{"id": "stream", "name": "流ID", "valueType": {"type": "string"}},
|
||||
{"id": "cloudRecordId", "name": "录像ID", "valueType": {"type": "int"}}
|
||||
],
|
||||
"output": {}
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"id": "deviceStatus",
|
||||
"mode": "r",
|
||||
"name": "设备状态",
|
||||
"config": false,
|
||||
"source": "device",
|
||||
"valueType": {"type": "enum", "elements": [{"text": "在线", "value": "online"}, {"text": "离线", "value": "offline"}]}
|
||||
},
|
||||
{
|
||||
"id": "manufacturer",
|
||||
"mode": "r",
|
||||
"name": "厂商",
|
||||
"config": false,
|
||||
"source": "device",
|
||||
"valueType": {"type": "string"}
|
||||
},
|
||||
{
|
||||
"id": "model",
|
||||
"mode": "r",
|
||||
"name": "型号",
|
||||
"config": false,
|
||||
"source": "device",
|
||||
"valueType": {"type": "string"}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -15,8 +15,6 @@
|
||||
<!-- 设备插件 -->
|
||||
<module>cn-universal-mqtt-protocol</module>
|
||||
<module>cn-universal-http-protocol</module>
|
||||
<!-- 视频平台协议子模块 -->
|
||||
<module>cn-wvp-protocol</module>
|
||||
</modules>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -163,11 +163,6 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-wvp-protocol</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.universal.iot</groupId>
|
||||
<artifactId>cn-universal-mqtt-protocol</artifactId>
|
||||
|
||||
Reference in New Issue
Block a user