package com.ycwl.basic.controller.pc; import com.fasterxml.jackson.databind.ObjectMapper; import com.ycwl.basic.annotation.IgnoreToken; 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.utils.ApiResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** * 设备视频连续性检查控制器 * 提供查询设备视频连续性检查结果的接口 * * @author Claude Code * @date 2025-09-01 */ @Slf4j @RestController @RequestMapping("/api/device/video-continuity") @RequiredArgsConstructor public class DeviceVideoContinuityController { private static final String REDIS_KEY_PREFIX = "device:video:continuity:"; private static final int CACHE_TTL_HOURS = 24; // 缓存24小时 private final RedisTemplate redisTemplate; private final ObjectMapper objectMapper; private final DeviceRepository deviceRepository; /** * 查询设备最近的视频连续性检查结果 * * @param deviceId 设备ID * @return 检查结果 */ @GetMapping("/{deviceId}") public ApiResponse getDeviceContinuityResult(@PathVariable Long deviceId) { log.info("查询设备 {} 的视频连续性检查结果", deviceId); try { String redisKey = REDIS_KEY_PREFIX + deviceId; String cacheJson = redisTemplate.opsForValue().get(redisKey); if (cacheJson == null) { log.warn("未找到设备 {} 的视频连续性检查结果", deviceId); return ApiResponse.buildResponse(404, null, "未找到该设备的检查结果,可能设备未配置存储或尚未执行检查"); } DeviceVideoContinuityCache cache = objectMapper.readValue(cacheJson, DeviceVideoContinuityCache.class); return ApiResponse.success(cache); } catch (Exception e) { log.error("查询设备 {} 视频连续性检查结果失败", deviceId, e); return ApiResponse.buildResponse(500, null, "查询失败: " + e.getMessage()); } } /** * 手动触发设备视频连续性检查 * 注意:仅用于测试和紧急情况,正常情况下由定时任务自动执行 * * @param deviceId 设备ID * @return 检查结果 */ @PostMapping("/{deviceId}/check") public ApiResponse manualCheck(@PathVariable Long deviceId) { log.info("手动触发设备 {} 的视频连续性检查", deviceId); return ApiResponse.success(null); } /** * 删除设备的视频连续性检查缓存 * 用于清理过期或错误的缓存数据 * * @param deviceId 设备ID * @return 删除结果 */ @DeleteMapping("/{deviceId}") public ApiResponse deleteContinuityCache(@PathVariable Long deviceId) { log.info("删除设备 {} 的视频连续性检查缓存", deviceId); try { String redisKey = REDIS_KEY_PREFIX + deviceId; Boolean deleted = redisTemplate.delete(redisKey); if (deleted != null && deleted) { return ApiResponse.success("缓存删除成功"); } else { return ApiResponse.buildResponse(404, null, "未找到该设备的缓存数据"); } } catch (Exception e) { log.error("删除设备 {} 视频连续性检查缓存失败", deviceId, e); return ApiResponse.buildResponse(500, null, "删除失败: " + e.getMessage()); } } /** * 外部工具上报视频连续性检查结果 * 通过设备编号(deviceNo)上报检查结果,无需认证 * * @param reportReq 上报请求 * @return 上报结果 */ @PostMapping("/report") @IgnoreToken public ApiResponse 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 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()); } } }