You've already forked FrameTour-BE
feat(pc): 添加外部工具上报视频连续性检查结果接口
- 添加 @IgnoreToken 注解支持无需认证的上报功能 - 新增 reportContinuityResult 方法处理外部工具上报请求 - 实现根据设备编号查询设备ID的逻辑 - 添加视频连续性检查结果的缓存存储功能 - 支持间隙信息的转换和存储到Redis - 设置24小时缓存TTL策略 - 完善日志记录和异常处理机制
This commit is contained in:
@@ -1,14 +1,24 @@
|
|||||||
package com.ycwl.basic.controller.pc;
|
package com.ycwl.basic.controller.pc;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.ycwl.basic.annotation.IgnoreToken;
|
||||||
import com.ycwl.basic.device.entity.common.DeviceVideoContinuityCache;
|
import com.ycwl.basic.device.entity.common.DeviceVideoContinuityCache;
|
||||||
|
import com.ycwl.basic.model.pc.device.entity.DeviceEntity;
|
||||||
|
import com.ycwl.basic.model.pc.device.req.VideoContinuityReportReq;
|
||||||
|
import com.ycwl.basic.repository.DeviceRepository;
|
||||||
import com.ycwl.basic.task.DeviceVideoContinuityCheckTask;
|
import com.ycwl.basic.task.DeviceVideoContinuityCheckTask;
|
||||||
import com.ycwl.basic.utils.ApiResponse;
|
import com.ycwl.basic.utils.ApiResponse;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备视频连续性检查控制器
|
* 设备视频连续性检查控制器
|
||||||
* 提供查询设备视频连续性检查结果的接口
|
* 提供查询设备视频连续性检查结果的接口
|
||||||
@@ -23,10 +33,12 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
public class DeviceVideoContinuityController {
|
public class DeviceVideoContinuityController {
|
||||||
|
|
||||||
private static final String REDIS_KEY_PREFIX = "device:video:continuity:";
|
private static final String REDIS_KEY_PREFIX = "device:video:continuity:";
|
||||||
|
private static final int CACHE_TTL_HOURS = 24; // 缓存24小时
|
||||||
|
|
||||||
private final RedisTemplate<String, String> redisTemplate;
|
private final RedisTemplate<String, String> redisTemplate;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
private final DeviceVideoContinuityCheckTask checkTask;
|
private final DeviceVideoContinuityCheckTask checkTask;
|
||||||
|
private final DeviceRepository deviceRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询设备最近的视频连续性检查结果
|
* 查询设备最近的视频连续性检查结果
|
||||||
@@ -103,4 +115,72 @@ public class DeviceVideoContinuityController {
|
|||||||
return ApiResponse.buildResponse(500, null, "删除失败: " + e.getMessage());
|
return ApiResponse.buildResponse(500, null, "删除失败: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 外部工具上报视频连续性检查结果
|
||||||
|
* 通过设备编号(deviceNo)上报检查结果,无需认证
|
||||||
|
*
|
||||||
|
* @param reportReq 上报请求
|
||||||
|
* @return 上报结果
|
||||||
|
*/
|
||||||
|
@PostMapping("/report")
|
||||||
|
@IgnoreToken
|
||||||
|
public ApiResponse<DeviceVideoContinuityCache> reportContinuityResult(
|
||||||
|
@Validated @RequestBody VideoContinuityReportReq reportReq) {
|
||||||
|
log.info("外部工具上报设备 {} 的视频连续性检查结果", reportReq.getDeviceNo());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 根据设备编号查询设备ID
|
||||||
|
DeviceEntity device = deviceRepository.getDeviceByDeviceNo(reportReq.getDeviceNo());
|
||||||
|
if (device == null) {
|
||||||
|
log.warn("设备编号 {} 不存在", reportReq.getDeviceNo());
|
||||||
|
return ApiResponse.buildResponse(404, null, "设备不存在: " + reportReq.getDeviceNo());
|
||||||
|
}
|
||||||
|
|
||||||
|
Long deviceId = device.getId();
|
||||||
|
|
||||||
|
// 2. 构建缓存对象
|
||||||
|
DeviceVideoContinuityCache cache = new DeviceVideoContinuityCache();
|
||||||
|
cache.setDeviceId(deviceId);
|
||||||
|
cache.setCheckTime(new Date());
|
||||||
|
cache.setStartTime(reportReq.getStartTime());
|
||||||
|
cache.setEndTime(reportReq.getEndTime());
|
||||||
|
cache.setSupport(reportReq.getSupport());
|
||||||
|
cache.setContinuous(reportReq.getContinuous());
|
||||||
|
cache.setTotalVideos(reportReq.getTotalVideos());
|
||||||
|
cache.setTotalDurationMs(reportReq.getTotalDurationMs());
|
||||||
|
cache.setMaxAllowedGapMs(reportReq.getMaxAllowedGapMs() != null
|
||||||
|
? reportReq.getMaxAllowedGapMs() : 2000L);
|
||||||
|
cache.setGapCount(reportReq.getGaps() != null ? reportReq.getGaps().size() : 0);
|
||||||
|
|
||||||
|
// 3. 转换间隙信息
|
||||||
|
if (reportReq.getGaps() != null && !reportReq.getGaps().isEmpty()) {
|
||||||
|
List<DeviceVideoContinuityCache.GapInfo> gapInfos = reportReq.getGaps().stream()
|
||||||
|
.map(gap -> new DeviceVideoContinuityCache.GapInfo(
|
||||||
|
gap.getBeforeFileName(),
|
||||||
|
gap.getAfterFileName(),
|
||||||
|
gap.getGapMs(),
|
||||||
|
gap.getGapStartTime(),
|
||||||
|
gap.getGapEndTime()
|
||||||
|
))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
cache.setGaps(gapInfos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 存储到Redis
|
||||||
|
String redisKey = REDIS_KEY_PREFIX + deviceId;
|
||||||
|
String cacheJson = objectMapper.writeValueAsString(cache);
|
||||||
|
redisTemplate.opsForValue().set(redisKey, cacheJson, CACHE_TTL_HOURS, TimeUnit.HOURS);
|
||||||
|
|
||||||
|
log.info("设备 {} (ID: {}) 视频连续性检查结果上报成功: continuous={}, videos={}, gaps={}",
|
||||||
|
reportReq.getDeviceNo(), deviceId, cache.getContinuous(),
|
||||||
|
cache.getTotalVideos(), cache.getGapCount());
|
||||||
|
|
||||||
|
return ApiResponse.success(cache);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("外部工具上报设备 {} 视频连续性检查结果失败", reportReq.getDeviceNo(), e);
|
||||||
|
return ApiResponse.buildResponse(500, null, "上报失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
package com.ycwl.basic.model.pc.device.req;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 外部工具上报视频连续性检查结果的请求DTO
|
||||||
|
*
|
||||||
|
* @author Claude Code
|
||||||
|
* @date 2025-12-30
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class VideoContinuityReportReq {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备编号(必填)
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "设备编号不能为空")
|
||||||
|
private String deviceNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查的开始时间(必填)
|
||||||
|
*/
|
||||||
|
@NotNull(message = "检查开始时间不能为空")
|
||||||
|
private Date startTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查的结束时间(必填)
|
||||||
|
*/
|
||||||
|
@NotNull(message = "检查结束时间不能为空")
|
||||||
|
private Date endTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否支持连续性检查(必填)
|
||||||
|
*/
|
||||||
|
@NotNull(message = "是否支持连续性检查不能为空")
|
||||||
|
private Boolean support;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频是否连续(必填)
|
||||||
|
* true: 所有间隙都在允许范围内
|
||||||
|
* false: 存在超出允许范围的间隙
|
||||||
|
*/
|
||||||
|
@NotNull(message = "视频是否连续不能为空")
|
||||||
|
private Boolean continuous;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频总数(必填)
|
||||||
|
*/
|
||||||
|
@NotNull(message = "视频总数不能为空")
|
||||||
|
private Integer totalVideos;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总时长,单位毫秒(必填)
|
||||||
|
*/
|
||||||
|
@NotNull(message = "总时长不能为空")
|
||||||
|
private Long totalDurationMs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 允许的最大间隙,单位毫秒(选填,默认2000ms)
|
||||||
|
*/
|
||||||
|
private Long maxAllowedGapMs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 间隙列表(选填,当continuous=false时应提供)
|
||||||
|
*/
|
||||||
|
private List<GapInfoReq> gaps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 间隙信息
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class GapInfoReq {
|
||||||
|
/**
|
||||||
|
* 前一个文件名
|
||||||
|
*/
|
||||||
|
private String beforeFileName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 后一个文件名
|
||||||
|
*/
|
||||||
|
private String afterFileName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 间隙时长,单位毫秒
|
||||||
|
*/
|
||||||
|
private Long gapMs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 间隙开始时间
|
||||||
|
*/
|
||||||
|
private Date gapStartTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 间隙结束时间
|
||||||
|
*/
|
||||||
|
private Date gapEndTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user