From ea3ce510d8374dd75a0fb15947c38379e759a71a Mon Sep 17 00:00:00 2001
From: Jerry Yan <792602257@qq.com>
Date: Fri, 7 Feb 2025 23:00:23 +0800
Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81vpt=E6=96=B9=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../basic/controller/vpt/VptController.java   |  52 ++++++++
 .../basic/controller/wvp/WvpController.java   |  12 +-
 .../com/ycwl/basic/device/DeviceFactory.java  |   3 +
 .../device/entity/common/FileObject.java      |   1 +
 .../vpt_passive/VptPassiveStorageConfig.java  |   8 ++
 .../device/enums/DeviceStoreTypeEnum.java     |   1 +
 .../operator/VptPassiveStorageOperator.java   | 117 ++++++++++++++++++
 .../operator/WvpPassiveStorageOperator.java   |   2 +
 .../com/ycwl/basic/task/VideoPieceGetter.java |  10 +-
 9 files changed, 191 insertions(+), 15 deletions(-)
 create mode 100644 src/main/java/com/ycwl/basic/controller/vpt/VptController.java
 create mode 100644 src/main/java/com/ycwl/basic/device/entity/vpt_passive/VptPassiveStorageConfig.java
 create mode 100644 src/main/java/com/ycwl/basic/device/operator/VptPassiveStorageOperator.java

diff --git a/src/main/java/com/ycwl/basic/controller/vpt/VptController.java b/src/main/java/com/ycwl/basic/controller/vpt/VptController.java
new file mode 100644
index 0000000..fa51a6c
--- /dev/null
+++ b/src/main/java/com/ycwl/basic/controller/vpt/VptController.java
@@ -0,0 +1,52 @@
+package com.ycwl.basic.controller.vpt;
+
+import com.ycwl.basic.annotation.IgnoreLogReq;
+import com.ycwl.basic.annotation.IgnoreToken;
+import com.ycwl.basic.device.entity.common.FileObject;
+import com.ycwl.basic.device.operator.VptPassiveStorageOperator;
+import com.ycwl.basic.device.operator.VptPassiveStorageOperator;
+import com.ycwl.basic.model.wvp.WvpSyncReqVo;
+import com.ycwl.basic.storage.StorageFactory;
+import com.ycwl.basic.storage.adapters.IStorageAdapter;
+import com.ycwl.basic.utils.ApiResponse;
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@Slf4j
+@IgnoreToken
+@RestController
+@Api(tags = "自定义切片工具对接接口")
+@RequestMapping("/vpt/v1/")
+public class VptController {
+
+    @IgnoreLogReq
+    @PostMapping("/scenic/{scenicId}/sync")
+    public ApiResponse<List<VptPassiveStorageOperator.Task>> sync(@PathVariable("scenicId") Long scenicId, @RequestBody WvpSyncReqVo reqVo) {
+        return ApiResponse.success(VptPassiveStorageOperator.getTaskListByScenicIdAndDeviceList(scenicId, reqVo.getDevices()));
+    }
+    @PostMapping("/scenic/{scenicId}/{taskId}/uploadUrl")
+    public String uploadUrl(@PathVariable("scenicId") Long scenicId, @PathVariable("taskId") Long taskId) {
+        IStorageAdapter adapter = StorageFactory.use("assets-ext");
+        return adapter.getUrlForUpload(VptPassiveStorageOperator.getUrlForTask(taskId));
+    }
+    @PostMapping("/scenic/{scenicId}/{taskId}/success")
+    public ApiResponse<String> success(@PathVariable("scenicId") Long scenicId, @PathVariable("taskId") Long taskId, @RequestBody FileObject fileObject) {
+        IStorageAdapter adapter = StorageFactory.use("assets-ext");
+        fileObject.setUrl(adapter.getUrl(VptPassiveStorageOperator.getUrlForTask(taskId)));
+        VptPassiveStorageOperator.onReceiveResult(taskId, fileObject);
+        return ApiResponse.success("success");
+    }
+    @PostMapping("/scenic/{scenicId}/{taskId}/failure")
+    public ApiResponse<String> fail(@PathVariable("scenicId") Long scenicId, @PathVariable("taskId") Long taskId) {
+        VptPassiveStorageOperator.onReceiveResult(taskId, null);
+        return ApiResponse.success("success");
+    }
+}
diff --git a/src/main/java/com/ycwl/basic/controller/wvp/WvpController.java b/src/main/java/com/ycwl/basic/controller/wvp/WvpController.java
index 363a2a1..4dfea9d 100644
--- a/src/main/java/com/ycwl/basic/controller/wvp/WvpController.java
+++ b/src/main/java/com/ycwl/basic/controller/wvp/WvpController.java
@@ -12,6 +12,7 @@ import com.ycwl.basic.utils.ApiResponse;
 import io.swagger.annotations.Api;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -28,11 +29,8 @@ import java.util.List;
 @RequestMapping("/wvp/v1/")
 public class WvpController {
 
-    private final DeviceService deviceService;
-
-    public WvpController(DeviceService deviceService) {
-        this.deviceService = deviceService;
-    }
+    @Autowired
+    private DeviceService deviceService;
 
     @IgnoreLogReq
     @PostMapping("/scenic/{scenicId}/sync")
@@ -49,9 +47,7 @@ public class WvpController {
     @PostMapping("/scenic/{scenicId}/{taskId}/success")
     public ApiResponse<String> success(@PathVariable("scenicId") Long scenicId, @PathVariable("taskId") Long taskId, @RequestBody FileObject fileObject) {
         IStorageAdapter adapter = StorageFactory.use("assets-ext");
-        if (StringUtils.isBlank(fileObject.getUrl())) {
-            fileObject.setUrl(adapter.getUrl(WvpPassiveStorageOperator.getUrlForTask(taskId)));
-        }
+        fileObject.setUrl(adapter.getUrl(WvpPassiveStorageOperator.getUrlForTask(taskId)));
         WvpPassiveStorageOperator.onReceiveResult(taskId, fileObject);
         return ApiResponse.success("success");
     }
diff --git a/src/main/java/com/ycwl/basic/device/DeviceFactory.java b/src/main/java/com/ycwl/basic/device/DeviceFactory.java
index 9108720..0b4e21d 100644
--- a/src/main/java/com/ycwl/basic/device/DeviceFactory.java
+++ b/src/main/java/com/ycwl/basic/device/DeviceFactory.java
@@ -7,6 +7,7 @@ import com.ycwl.basic.device.enums.DeviceStoreTypeEnum;
 import com.ycwl.basic.device.operator.AliOssStorageOperator;
 import com.ycwl.basic.device.operator.IDeviceStorageOperator;
 import com.ycwl.basic.device.operator.LocalStorageOperator;
+import com.ycwl.basic.device.operator.VptPassiveStorageOperator;
 import com.ycwl.basic.device.operator.WvpActiveStorageOperator;
 import com.ycwl.basic.device.operator.WvpPassiveStorageOperator;
 import com.ycwl.basic.model.pc.device.entity.DeviceConfigEntity;
@@ -24,6 +25,8 @@ public class DeviceFactory {
             operator = new WvpActiveStorageOperator(config.getStoreConfigJson());
         } else if (Integer.valueOf(DeviceStoreTypeEnum.WVP_PASSIVE.getType()).equals(config.getStoreType())) {
             operator = new WvpPassiveStorageOperator(config.getStoreConfigJson());
+        } else if (Integer.valueOf(DeviceStoreTypeEnum.VPT_PASSIVE.getType()).equals(config.getStoreType())) {
+            operator = new VptPassiveStorageOperator(config.getStoreConfigJson());
         } else if (Integer.valueOf(DeviceStoreTypeEnum.LOCAL.getType()).equals(config.getStoreType())) {
             operator = new LocalStorageOperator(config.getStoreConfigJson());
         }
diff --git a/src/main/java/com/ycwl/basic/device/entity/common/FileObject.java b/src/main/java/com/ycwl/basic/device/entity/common/FileObject.java
index 27d51bd..aa22dd5 100644
--- a/src/main/java/com/ycwl/basic/device/entity/common/FileObject.java
+++ b/src/main/java/com/ycwl/basic/device/entity/common/FileObject.java
@@ -9,6 +9,7 @@ public class FileObject {
     private String path;
     private String name;
     private String url;
+    private boolean exact = false;
     private boolean needDownload = false;
     private Date createTime;
     private Date endTime;
diff --git a/src/main/java/com/ycwl/basic/device/entity/vpt_passive/VptPassiveStorageConfig.java b/src/main/java/com/ycwl/basic/device/entity/vpt_passive/VptPassiveStorageConfig.java
new file mode 100644
index 0000000..2f7aae5
--- /dev/null
+++ b/src/main/java/com/ycwl/basic/device/entity/vpt_passive/VptPassiveStorageConfig.java
@@ -0,0 +1,8 @@
+package com.ycwl.basic.device.entity.vpt_passive;
+
+import lombok.Data;
+
+@Data
+public class VptPassiveStorageConfig {
+    private String deviceNo;
+}
diff --git a/src/main/java/com/ycwl/basic/device/enums/DeviceStoreTypeEnum.java b/src/main/java/com/ycwl/basic/device/enums/DeviceStoreTypeEnum.java
index 59f96bd..96628cc 100644
--- a/src/main/java/com/ycwl/basic/device/enums/DeviceStoreTypeEnum.java
+++ b/src/main/java/com/ycwl/basic/device/enums/DeviceStoreTypeEnum.java
@@ -9,6 +9,7 @@ public enum DeviceStoreTypeEnum {
     ALI_OSS(1, "阿里云OSS"),
     WVP_ACTIVE(3, "WVP主动模式"),
     WVP_PASSIVE(4, "WVP被动模式"),
+    VPT_PASSIVE(5, "VPT被动模式"),
     LOCAL(2, "本地文件");
 
     private final int type;
diff --git a/src/main/java/com/ycwl/basic/device/operator/VptPassiveStorageOperator.java b/src/main/java/com/ycwl/basic/device/operator/VptPassiveStorageOperator.java
new file mode 100644
index 0000000..94e27b0
--- /dev/null
+++ b/src/main/java/com/ycwl/basic/device/operator/VptPassiveStorageOperator.java
@@ -0,0 +1,117 @@
+package com.ycwl.basic.device.operator;
+
+import com.alibaba.fastjson.JSON;
+import com.ycwl.basic.device.entity.common.FileObject;
+import com.ycwl.basic.device.entity.vpt_passive.VptPassiveStorageConfig;
+import com.ycwl.basic.device.entity.wvp_passive.WvpPassiveStorageConfig;
+import com.ycwl.basic.model.wvp.WvpSyncReqVo;
+import com.ycwl.basic.storage.utils.StorageUtil;
+import com.ycwl.basic.utils.SnowFlakeUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+@Slf4j
+public class VptPassiveStorageOperator extends ADeviceStorageOperator {
+    public static class Task {
+        public Long taskId;
+        public Long scenicId;
+        public Long deviceId;
+        public String deviceNo;
+        public Date startTime;
+        public Date endTime;
+    }
+
+    private static List<Task> taskList = Collections.synchronizedList(new ArrayList<>());
+    private static ConcurrentHashMap<Long, FileObject> fileListMap = new ConcurrentHashMap<>();
+
+    private VptPassiveStorageConfig config;
+
+    public VptPassiveStorageOperator(String configJson) {
+        loadConfig(configJson);
+    }
+
+    public static String getUrlForTask(Long taskId) {
+        return StorageUtil.joinPath("video-source", taskId.toString() + ".mp4");
+    }
+
+    public static void onReceiveResult(Long taskId, FileObject fileObject) {
+        if (fileObject == null) {
+            log.info("任务{}获取视频失败!", taskId);
+            fileListMap.put(taskId, new FileObject());
+        } else {
+            fileListMap.put(taskId, fileObject);
+        }
+    }
+
+    public static List<Task> getTaskListByScenicIdAndDeviceList(Long scenicId, List<WvpSyncReqVo.DeviceItem> deviceNos) {
+        List<Task> scenicTask = taskList.stream()
+                .filter(task -> task.scenicId.equals(scenicId))
+                .filter(task -> deviceNos.stream().anyMatch(deviceNo -> deviceNo.getDeviceNo().equals(task.deviceNo)))
+                .collect(Collectors.toList());
+        if (scenicTask.isEmpty()) {
+            return Collections.emptyList();
+        }
+        taskList.removeAll(scenicTask);
+        return scenicTask;
+    }
+
+    @Override
+    public void loadConfig(String configJson) {
+        this.config = JSON.parseObject(configJson, VptPassiveStorageConfig.class);
+    }
+
+    @Override
+    public List<FileObject> getFileListByDtRange(Date startDate, Date endDate) {
+        Task task = new Task();
+        task.taskId = SnowFlakeUtil.getLongId();
+        task.scenicId = device.getScenicId();
+        task.deviceId = device.getId();
+        if (StringUtils.isNotBlank(config.getDeviceNo())) {
+            task.deviceNo = config.getDeviceNo();
+        } else {
+            task.deviceNo = device.getNo2();
+        }
+        task.startTime = startDate;
+        task.endTime = endDate;
+        taskList.add(task);
+        Date taskStartTime = new Date();
+        while (true) {
+            if (new Date().getTime() - taskStartTime.getTime() > 80000L) {
+                log.info("任务{}获取视频超时!", task.taskId);
+                fileListMap.remove(task.taskId);
+                return Collections.emptyList();
+            }
+            if (fileListMap.containsKey(task.taskId)) {
+                FileObject fileObject = fileListMap.get(task.taskId);
+                if (StringUtils.isNotBlank(fileObject.getUrl())) {
+                    log.info("任务{}获取视频成功!{}", task.taskId, fileObject);
+                    fileListMap.remove(task.taskId);
+                    fileObject.setExact(true);
+                    return Collections.singletonList(fileObject);
+                } else {
+                    log.info("任务{}获取视频失败!", task.taskId);
+                    return Collections.emptyList();
+                }
+            } else {
+                try {
+                    Thread.sleep(1000L);
+                } catch (InterruptedException e) {
+                    return Collections.emptyList();
+                }
+            }
+        }
+    }
+
+    @Override
+    public boolean removeFilesBeforeDate(Date date) {
+        // 不做删除
+        return false;
+    }
+}
diff --git a/src/main/java/com/ycwl/basic/device/operator/WvpPassiveStorageOperator.java b/src/main/java/com/ycwl/basic/device/operator/WvpPassiveStorageOperator.java
index 7fc2d76..8b28940 100644
--- a/src/main/java/com/ycwl/basic/device/operator/WvpPassiveStorageOperator.java
+++ b/src/main/java/com/ycwl/basic/device/operator/WvpPassiveStorageOperator.java
@@ -80,6 +80,7 @@ public class WvpPassiveStorageOperator extends ADeviceStorageOperator {
         while (true) {
             if (new Date().getTime() - taskStartTime.getTime() > 80000L) {
                 log.info("任务{}获取视频超时!", task.taskId);
+                fileListMap.remove(task.taskId);
                 return Collections.emptyList();
             }
             if (fileListMap.containsKey(task.taskId)) {
@@ -87,6 +88,7 @@ public class WvpPassiveStorageOperator extends ADeviceStorageOperator {
                 if (StringUtils.isNotBlank(fileObject.getUrl())) {
                     log.info("任务{}获取视频成功!{}", task.taskId, fileObject);
                     fileListMap.remove(task.taskId);
+                    fileObject.setExact(true);
                     return Collections.singletonList(fileObject);
                 } else {
                     log.info("任务{}获取视频失败!", task.taskId);
diff --git a/src/main/java/com/ycwl/basic/task/VideoPieceGetter.java b/src/main/java/com/ycwl/basic/task/VideoPieceGetter.java
index a2db559..7959533 100644
--- a/src/main/java/com/ycwl/basic/task/VideoPieceGetter.java
+++ b/src/main/java/com/ycwl/basic/task/VideoPieceGetter.java
@@ -151,11 +151,9 @@ public class VideoPieceGetter {
                             log.info("查询到可用的文件: {}", listByDtRange);
                             // 如果完全一致,就不需要裁切
                             String url;
-                            if (
-                                    listByDtRange.size() != 1 ||
-                                    (listByDtRange.get(0).getCreateTime().getTime() != faceSample.getCreateAt().getTime() - cutPre.multiply(BigDecimal.valueOf(1000)).longValue()
-                                            || listByDtRange.get(0).getEndTime().getTime() != faceSample.getCreateAt().getTime() + cutPost.multiply(BigDecimal.valueOf(1000)).longValue())
-                            ) {
+                            if (listByDtRange.size() == 1 && listByDtRange.get(0).isExact()) {
+                                url = listByDtRange.get(0).getUrl();
+                            } else {
                                 long offset = faceSample.getCreateAt().getTime() - cutPre.multiply(BigDecimal.valueOf(1000)).longValue() - listByDtRange.get(0).getCreateTime().getTime();
                                 FfmpegTask ffmpegTask = new FfmpegTask();
                                 ffmpegTask.setFileList(listByDtRange);
@@ -173,8 +171,6 @@ public class VideoPieceGetter {
                                 url = adapter.uploadFile(outFile, "video-source", outFile.getName());
                                 // 上传成功后删除文件
                                 outFile.delete();
-                            } else {
-                                url = listByDtRange.get(0).getUrl();
                             }
                             SourceEntity imgSource = sourceMapper.findBySampleId(faceSample.getId());
                             SourceEntity sourceEntity = new SourceEntity();