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> 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 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 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 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 taskList = Collections.synchronizedList(new ArrayList<>()); + private static ConcurrentHashMap 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 getTaskListByScenicIdAndDeviceList(Long scenicId, List deviceNos) { + List 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 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();