|
@@ -1,8 +1,10 @@
|
|
|
package com.ozs.controller.upload;
|
|
|
|
|
|
import com.alibaba.fastjson2.JSON;
|
|
|
+import com.alibaba.fastjson2.JSONArray;
|
|
|
import com.alibaba.fastjson2.JSONObject;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
import com.ozs.common.annotation.SdkLog;
|
|
|
import com.ozs.common.constant.Constants;
|
|
|
import com.ozs.common.core.redis.RedisCache;
|
|
@@ -11,6 +13,7 @@ import com.ozs.common.enums.BusinessTypeSdk;
|
|
|
import com.ozs.common.utils.ApiTokenUtils;
|
|
|
import com.ozs.common.utils.HttpClientUtil;
|
|
|
import com.ozs.common.utils.StringUtils;
|
|
|
+import com.ozs.common.utils.http.HttpUtils;
|
|
|
import com.ozs.common.utils.stateSecrets.SM4Utils;
|
|
|
import com.ozs.service.entity.BaseCameraManagement;
|
|
|
import com.ozs.service.entity.BaseDeviceDynamicManagement;
|
|
@@ -28,6 +31,7 @@ import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.beans.BeanUtils;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
+import org.springframework.data.redis.core.SetOperations;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
import org.springframework.util.ObjectUtils;
|
|
|
import org.springframework.web.bind.annotation.PostMapping;
|
|
@@ -39,7 +43,16 @@ import javax.annotation.Resource;
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
import java.text.ParseException;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.time.Instant;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.OffsetDateTime;
|
|
|
+import java.time.ZoneOffset;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
import java.util.*;
|
|
|
+import java.util.concurrent.CompletableFuture;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
|
/**
|
|
|
* 1. 获取身份认证控制层
|
|
@@ -70,7 +83,12 @@ public class GeoHazardMonitorTokenController {
|
|
|
private RedisCache redisCache;
|
|
|
@Autowired
|
|
|
private RedisService redisService;
|
|
|
-
|
|
|
+ @Value("${base.webUrl}")
|
|
|
+ private String webUrl;
|
|
|
+ @Value("${base.wsUrl}")
|
|
|
+ private String wsUrl;
|
|
|
+ @Value("${base.recordUrl}")
|
|
|
+ private String recordUrl;
|
|
|
|
|
|
/**
|
|
|
* 获取web访问令牌
|
|
@@ -211,15 +229,12 @@ public class GeoHazardMonitorTokenController {
|
|
|
respMsgAlarmVo.setCameraCode(reqMsgAlarmVo.getAlarmCamera());
|
|
|
respMsgAlarmVo.setIsCancel(2);
|
|
|
respMsgAlarmVo.setCancelTime(null);
|
|
|
- // 记录要合成报警视频的报警id
|
|
|
-// Map<String, Object> map = redisCache.getCacheMap(RedisConstant.key);
|
|
|
-// if (ObjectUtils.isEmpty(map)) {
|
|
|
-// map = new HashMap<>();
|
|
|
-// }
|
|
|
-// map.put(msgAlarm.getAlarmId(), msgAlarm.getAlarmTime());
|
|
|
-// log.info("map:{}", map);
|
|
|
-// redisCache.deleteObject(RedisConstant.key);
|
|
|
-// redisCache.setCacheMap(RedisConstant.key, map);
|
|
|
+ // 合成报警视频
|
|
|
+ CompletableFuture.runAsync(() -> {
|
|
|
+ String alarmUrl = alarmHistoryPlay(baseCameraManagement.getCameraCode(), baseCameraManagement.getChannel(), msgAlarm.getAlarmTime());
|
|
|
+ msgAlarm.setReleasedUrl(alarmUrl);
|
|
|
+ msgAlarmService.updateById(msgAlarm);
|
|
|
+ });
|
|
|
} else {
|
|
|
BeanUtils.copyProperties(reqMsgAlarmVo, msgAlarmVice);
|
|
|
String[] imageUrl = reqMsgAlarmVo.getImageUrls();
|
|
@@ -296,17 +311,13 @@ public class GeoHazardMonitorTokenController {
|
|
|
BaseUser admin = baseUserService.getUserName("admin");
|
|
|
lw.eq(BaseDeviceDynamicManagement::getCameraCode, reqDeviceVo.getCameraCode());
|
|
|
BaseDeviceDynamicManagement baseDynamicManagement = baseDeviceDynamicManagementService.getOne(lw);
|
|
|
- if (ObjectUtils.isEmpty(baseDynamicManagement)) {
|
|
|
- jsonObject.put("resultCode", 0);
|
|
|
- jsonObject.put("message", "相机编码不存在!");
|
|
|
- return SM4Utils.encryptData_ECB(JSONObject.toJSONString(jsonObject), "4370780c9a8c43e5");
|
|
|
- }
|
|
|
baseDynamicManagement.setElectricity(reqDeviceVo.getElectricity());
|
|
|
if (reqDeviceVo.getTime().toString().length() == 10) {
|
|
|
reqDeviceVo.setTime(reqDeviceVo.getTime() * 1000);
|
|
|
}
|
|
|
baseDynamicManagement.setUpdateTime(new Date(reqDeviceVo.getTime()));
|
|
|
baseDynamicManagement.setUpdateBy(admin.getUserId());
|
|
|
+ baseDynamicManagement.setElectricityState(2);
|
|
|
log.info("reqDeviceVo:{}", baseDynamicManagement);
|
|
|
if (baseDeviceDynamicManagementService.updateById(baseDynamicManagement)) {
|
|
|
jsonObject.put("resultCode", 1);
|
|
@@ -323,6 +334,194 @@ public class GeoHazardMonitorTokenController {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 报警视频回放新
|
|
|
+ * @param channel
|
|
|
+ * @param cameraCode
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public String alarmHistoryPlay(String channel,String cameraCode,Date alarmDate) {
|
|
|
+ // 获取当前时间
|
|
|
+ // 将Date对象转换为Instant对象
|
|
|
+ Instant instant = alarmDate.toInstant();
|
|
|
+
|
|
|
+ OffsetDateTime currentTime = instant.atOffset(ZoneOffset.ofHours(8));
|
|
|
+ log.info("currentTime------------->" + currentTime);
|
|
|
+ // 获取报警前的时间
|
|
|
+ OffsetDateTime beforeTenMinutes = currentTime.minusMinutes(Long.parseLong("5"));
|
|
|
+ log.info("beforeTenMinutes------------->" + beforeTenMinutes);
|
|
|
+ // 获取报警后的时间
|
|
|
+ OffsetDateTime afterTenMinutes = currentTime.plusMinutes(Long.parseLong("5"));
|
|
|
+ log.info("afterTenMinutes------------->" + afterTenMinutes);
|
|
|
+ // 格式化时间
|
|
|
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
|
|
+ // 将字符串解析为LocalDateTime对象(指定时区为东八区)
|
|
|
+ LocalDateTime startTime = LocalDateTime.parse(beforeTenMinutes.format(formatter), formatter).atOffset(ZoneOffset.ofHours(8)).toLocalDateTime();
|
|
|
+ LocalDateTime endTime = LocalDateTime.parse(afterTenMinutes.format(formatter), formatter).atOffset(ZoneOffset.ofHours(8)).toLocalDateTime();
|
|
|
+ // 将LocalDateTime转换为秒数
|
|
|
+ long seconds = startTime.toEpochSecond(ZoneOffset.ofHours(8));
|
|
|
+ long second = endTime.toEpochSecond(ZoneOffset.ofHours(8));
|
|
|
+ try {
|
|
|
+ TimeUnit.SECONDS.sleep(65 * 5);
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ String result;
|
|
|
+ String url = webUrl + "/gb28181/api/invite?id=" + cameraCode + "&channel=" + channel + "&startTime=" + seconds + "&endTime=" + second;
|
|
|
+ try {
|
|
|
+ result = HttpClientUtil.get(url);
|
|
|
+ if ("200".equals(result)) {
|
|
|
+ String path = apiSummary(cameraCode + "/" + channel+"/"+seconds+"-"+second);
|
|
|
+ if (org.apache.commons.lang3.StringUtils.isNotEmpty(path)){
|
|
|
+ String codes = HttpUtils.sendGet(startRecording(cameraCode, "playback"));
|
|
|
+ log.info("codes------>" + codes);
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
+ // 使用SimpleDateFormat将Date对象格式化为指定样式的字符串
|
|
|
+ String formattedDate = sdf.format(alarmDate);
|
|
|
+ log.info("-----formattedDate-----------" + formattedDate);
|
|
|
+ String records = getQueryRecords(alarmDate + "/playback", formattedDate,"flv");
|
|
|
+ log.info("-----records-----------" + records);
|
|
|
+ return records;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.info(e.getMessage());
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 调用流媒体获取流列表信息
|
|
|
+ *
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public String apiSummary(String path) {
|
|
|
+ int maxRetries = 5;
|
|
|
+ int retryCount = 0;
|
|
|
+ boolean success = false;
|
|
|
+ JSONArray streamArray = null;
|
|
|
+ while (!success && retryCount < maxRetries) {
|
|
|
+ try {
|
|
|
+ String data = HttpClientUtil.gets(webUrl + "/api/summary");
|
|
|
+ log.info("data------------------>" + data);
|
|
|
+ if (org.apache.commons.lang3.StringUtils.isNotEmpty(data)) {
|
|
|
+ JSONObject jsonObject = JSONObject.parseObject(data);
|
|
|
+ streamArray = jsonObject.getJSONArray("Streams");
|
|
|
+ success = true;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.info("Exception caught: " + e.getMessage());
|
|
|
+ // 增加重试计数
|
|
|
+ retryCount++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!ObjectUtils.isEmpty(streamArray)) {
|
|
|
+ for (int i = 0; i < streamArray.size(); i++) {
|
|
|
+ JSONObject subObj = streamArray.getJSONObject(i);
|
|
|
+ String streamPath = subObj.getString("StreamPath");
|
|
|
+ if (streamPath.equals(path)) {
|
|
|
+ //取出VideoTracks
|
|
|
+ JSONArray videoJson = subObj.getJSONArray("VideoTracks");
|
|
|
+ if (videoJson != null && !videoJson.isEmpty()) {
|
|
|
+ return wsUrl + "/ws/" + streamPath;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 开启录制功能
|
|
|
+ *
|
|
|
+ * @param cameraCode 相机编码
|
|
|
+ * @param channel 相机通道
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public String startRecording(String cameraCode, String channel) {
|
|
|
+ log.info("-----startRecording------" + webUrl + "/recordpro/api/start?streamPath=" + cameraCode + "/" + channel + "&type=mp4");
|
|
|
+ return webUrl + "/recordpro/api/start?streamPath=" + cameraCode + "/" + channel + "&type=mp4";
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据设备id和通道ID和日期获取设备录像文件
|
|
|
+ *
|
|
|
+ */
|
|
|
+ public String getQueryRecords(String streamPath, String date, String type) {
|
|
|
+
|
|
|
+ log.info("-----getQueryRecords------streamPath-----" + streamPath);
|
|
|
+ log.info("-----getQueryRecords-------date----" + date);
|
|
|
+ log.info("-----getQueryRecords-------type----" + type);
|
|
|
+ try {
|
|
|
+ TimeUnit.SECONDS.sleep(60 * 10);
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ String param = "{\n" +
|
|
|
+ " \"streamPath\":\"" + streamPath + "\",\n" +
|
|
|
+ " \"date\":\"" + date + "\",\n" +
|
|
|
+ " \"type\":\"" + type + "\"\n" +
|
|
|
+ "}";
|
|
|
+
|
|
|
+ String jsonStr = HttpUtils.sendPost(webUrl + "/recordpro/api/query/records", param);
|
|
|
+ log.info("-----getQueryRecords-------jsonStr----" + jsonStr);
|
|
|
+ JSONObject jsonObj = JSON.parseObject(jsonStr);
|
|
|
+ log.info("-----getQueryRecords-------jsonObj----" + jsonObj);
|
|
|
+ JSONArray resultArray = jsonObj.getJSONArray("result");
|
|
|
+ log.info("-----getQueryRecords-------resultArray----" + resultArray);
|
|
|
+
|
|
|
+ if (!ObjectUtils.isEmpty(resultArray)) {
|
|
|
+ ArrayList<String> list = new ArrayList<>();
|
|
|
+ for (int i = 0; i < resultArray.size(); i++) {
|
|
|
+ JSONObject subObj = resultArray.getJSONObject(i);
|
|
|
+ String size = subObj.getString("Size");
|
|
|
+ String path = subObj.getString("Path");
|
|
|
+ Object created = subObj.get("Created");
|
|
|
+ //从缓存取出预拉的流的集合
|
|
|
+ log.info("size------------------>" + size);
|
|
|
+ log.info("path------------------>" + path);
|
|
|
+ log.info("created------------------>" + created);
|
|
|
+ //判断主码流是否订阅者是2
|
|
|
+ if (Integer.valueOf(size) > 1000) {
|
|
|
+ list.add(created + "=" + path);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ String records = records(list);
|
|
|
+ log.info("-----getQueryRecords-------records----" + records);
|
|
|
+ String[] split = records.split("=");
|
|
|
+ return recordUrl + Constants.RESOURCE_PREFIX + split[1];
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String records(List<String> objects) {
|
|
|
+
|
|
|
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX");
|
|
|
+ OffsetDateTime maxTime = null;
|
|
|
+
|
|
|
+ for (String timeString : objects) {
|
|
|
+ String[] split = timeString.split("=");
|
|
|
+ OffsetDateTime time = OffsetDateTime.parse(split[0], formatter);
|
|
|
+ if (maxTime == null || time.isAfter(maxTime)) {
|
|
|
+ maxTime = time;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Iterator<String> iter = objects.iterator();
|
|
|
+ while (iter.hasNext()) {
|
|
|
+ String timeString = iter.next();
|
|
|
+ String[] split = timeString.split("=");
|
|
|
+ OffsetDateTime time = OffsetDateTime.parse(split[0], formatter);
|
|
|
+ if (!time.isEqual(maxTime)) {
|
|
|
+ iter.remove();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return objects.get(0);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
public static void main(String[] args) {
|
|
|
// String parameter = "rjW9XcaNdY1M2rtUj4rYsW+3nqcJUDBuE6Orqvd4vyMhOh3fQGPNava0aOcUA7h0wuxoQtHE3bUP5imxqbRd/iBT7QDkrqtdPP5QLEhQodT8v0l8YMMtb7yohAJfEThLJOJ8Odl0kuTJXEJBMD8pE//K9KpRGDY4eEKPdS26YywnIl6HwK9EweYxVGtfJOP3/N7lwum2ROv7iqYgefXd/IxGEG8eVqEUutGKpeCcM0dds/N3Uq5ri/k/BrpaKcV96GnlR4pWkBjWOW08eGJ78shuUJkC682294W4RUp3NPPR7OTErtVAh65or8pB+RGDZuZQlUJ/QVpWp2ZdCrN5Wjqsi1TIa00oGQbYmMixOF0=";
|
|
|
// String s = SM4Utils.decryptData_ECB(parameter, "4370780c9a8c43e5");
|