diff --git a/src/main/java/com/ycwl/basic/controller/mobile/AppGoodsController.java b/src/main/java/com/ycwl/basic/controller/mobile/AppGoodsController.java index 7f74b568..8ac7e1b8 100644 --- a/src/main/java/com/ycwl/basic/controller/mobile/AppGoodsController.java +++ b/src/main/java/com/ycwl/basic/controller/mobile/AppGoodsController.java @@ -99,4 +99,16 @@ public class AppGoodsController { JwtInfo worker = JwtTokenUtil.getWorker(); return ApiResponse.success(goodsService.getTaskStatusByTemplateId(faceId, templateId)); } + + /** + * 检查视频是否可更新 + * + * @param videoId 视频ID + * @return 视频更新检查结果 + */ + @GetMapping("/video/{videoId}/updateCheck") + public ApiResponse checkVideoUpdate(@PathVariable("videoId") Long videoId) { + VideoUpdateCheckVO result = goodsService.checkVideoUpdate(videoId); + return ApiResponse.success(result); + } } diff --git a/src/main/java/com/ycwl/basic/model/mobile/goods/VideoGoodsDetailVO.java b/src/main/java/com/ycwl/basic/model/mobile/goods/VideoGoodsDetailVO.java index 3fbc6fcc..f56688a9 100644 --- a/src/main/java/com/ycwl/basic/model/mobile/goods/VideoGoodsDetailVO.java +++ b/src/main/java/com/ycwl/basic/model/mobile/goods/VideoGoodsDetailVO.java @@ -30,6 +30,7 @@ public class VideoGoodsDetailVO { private Integer sourceType; // 商品id goodsType=1时为videoId,goodsType=2时为sourceId private Long goodsId; + private Long templateId; // 模版封面图片 private String templateCoverUrl; // 图片文件存储地址 diff --git a/src/main/java/com/ycwl/basic/service/mobile/GoodsService.java b/src/main/java/com/ycwl/basic/service/mobile/GoodsService.java index 3ef19c8d..5b243a5f 100644 --- a/src/main/java/com/ycwl/basic/service/mobile/GoodsService.java +++ b/src/main/java/com/ycwl/basic/service/mobile/GoodsService.java @@ -50,4 +50,11 @@ public interface GoodsService { List sourceGoodsListDownload(GoodsReqQuery query); Integer sourceGoodsCount(GoodsReqQuery query); + + /** + * 检查视频是否可更新 + * @param videoId 视频ID + * @return 视频更新检查结果 + */ + VideoUpdateCheckVO checkVideoUpdate(Long videoId); } diff --git a/src/main/java/com/ycwl/basic/service/mobile/impl/GoodsServiceImpl.java b/src/main/java/com/ycwl/basic/service/mobile/impl/GoodsServiceImpl.java index af05250e..51f5130f 100644 --- a/src/main/java/com/ycwl/basic/service/mobile/impl/GoodsServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/mobile/impl/GoodsServiceImpl.java @@ -31,14 +31,19 @@ import com.ycwl.basic.model.pc.source.resp.SourceRespVO; import com.ycwl.basic.model.pc.task.entity.TaskEntity; import com.ycwl.basic.model.pc.template.resp.TemplateRespVO; import com.ycwl.basic.model.pc.video.entity.MemberVideoEntity; +import com.ycwl.basic.model.pc.video.entity.VideoEntity; import com.ycwl.basic.model.pc.video.req.VideoReqQuery; import com.ycwl.basic.model.pc.video.resp.VideoRespVO; import com.ycwl.basic.repository.DeviceRepository; import com.ycwl.basic.repository.FaceRepository; import com.ycwl.basic.repository.ScenicRepository; +import com.ycwl.basic.repository.VideoRepository; import com.ycwl.basic.repository.VideoTaskRepository; import com.ycwl.basic.service.mobile.GoodsService; import com.ycwl.basic.repository.TemplateRepository; +import com.ycwl.basic.repository.SourceRepository; +import com.ycwl.basic.biz.TemplateBiz; +import com.ycwl.basic.config.VideoUpdateConfig; import com.ycwl.basic.service.task.TaskService; import com.ycwl.basic.storage.StorageFactory; import com.ycwl.basic.storage.adapters.IStorageAdapter; @@ -75,6 +80,8 @@ public class GoodsServiceImpl implements GoodsService { @Autowired private TemplateRepository templateRepository; @Autowired + private VideoRepository videoRepository; + @Autowired private VideoTaskRepository videoTaskRepository; @Autowired private TaskService taskTaskService; @@ -90,6 +97,12 @@ public class GoodsServiceImpl implements GoodsService { private DeviceRepository deviceRepository; @Autowired private CouponBiz couponBiz; + @Autowired + private SourceRepository sourceRepository; + @Autowired + private TemplateBiz templateBiz; + @Autowired + private VideoUpdateConfig videoUpdateConfig; public ApiResponse> goodsList(GoodsReqQuery query) { Long scenicId = query.getScenicId(); @@ -260,6 +273,7 @@ public class GoodsServiceImpl implements GoodsService { goodsDetailVO.setGoodsType(0); goodsDetailVO.setGoodsId(videoRespVO.getId()); goodsDetailVO.setVideoUrl(videoRespVO.getVideoUrl()); + goodsDetailVO.setTemplateId(videoRespVO.getTemplateId()); goodsDetailVO.setTemplateCoverUrl(videoRespVO.getTemplateCoverUrl()); goodsDetailVO.setCreateTime(videoRespVO.getCreateTime()); goodsDetailVO.setHeight(videoRespVO.getHeight()); @@ -767,4 +781,147 @@ public class GoodsServiceImpl implements GoodsService { sourceReqQuery.setFaceId(query.getFaceId()); return sourceMapper.countUser(sourceReqQuery); } + + @Override + public VideoUpdateCheckVO checkVideoUpdate(Long videoId) { + VideoUpdateCheckVO result = new VideoUpdateCheckVO(); + result.setVideoId(videoId); + + if (!videoUpdateConfig.isEnabled()) { + log.info("视频更新检查功能已禁用"); + result.setCanUpdate(false); + return result; + } + + VideoEntity video = videoRepository.getVideo(videoId); + if (video == null) { + log.error("视频不存在: videoId={}", videoId); + result.setCanUpdate(false); + return result; + } + + Long taskId = video.getTaskId(); + TaskEntity task = videoTaskRepository.getTaskById(taskId); + if (task == null) { + log.error("关联任务不存在: videoId={}, taskId={}", videoId, taskId); + result.setCanUpdate(false); + return result; + } + + result.setTaskId(taskId); + + result.setFaceId(task.getFaceId()); + result.setTemplateId(task.getTemplateId()); + + try { + Map originalTaskParams = JacksonUtil.parseObject(task.getTaskParams(), Map.class); + if (originalTaskParams == null) { + log.error("原始任务参数解析失败: taskId={}", taskId); + result.setCanUpdate(false); + return result; + } + + int originalSegmentCount = calculateSegmentCount(originalTaskParams); + result.setOriginalSegmentCount(originalSegmentCount); + + Map> currentTaskParams = sourceRepository.getTaskParams(task.getFaceId(), task.getTemplateId()); + if (currentTaskParams.isEmpty()) { + log.info("当前没有可用的任务参数: faceId={}, templateId={}", task.getFaceId(), task.getTemplateId()); + result.setCanUpdate(false); + result.setTotalSegmentCount(0); + result.setNewSegmentCount(0); + return result; + } + + Map> filteredTaskParams = templateBiz.filterTaskParams(task.getTemplateId(), currentTaskParams); + int currentSegmentCount = calculateSegmentCount(filteredTaskParams); + result.setTotalSegmentCount(currentSegmentCount); + + boolean hasNewSegments = videoUpdateConfig.isDetectChangesAsNew() + ? hasNewSegments(originalTaskParams, filteredTaskParams) + : currentSegmentCount > originalSegmentCount; + int newSegmentCount = Math.max(0, currentSegmentCount - originalSegmentCount); + + boolean canUpdate = hasNewSegments && newSegmentCount >= videoUpdateConfig.getMinNewSegmentCount(); + result.setCanUpdate(canUpdate); + result.setNewSegmentCount(newSegmentCount); + + log.info("视频更新检查完成: videoId={}, taskId={}, canUpdate={}, newSegmentCount={}, originalCount={}, currentCount={}", + videoId, taskId, result.isCanUpdate(), newSegmentCount, originalSegmentCount, currentSegmentCount); + + } catch (Exception e) { + log.error("检查视频更新失败: videoId={}, taskId={}", videoId, taskId, e); + result.setCanUpdate(false); + } + + return result; + } + + private int calculateSegmentCount(Map taskParams) { + if (taskParams == null || taskParams.isEmpty()) { + return 0; + } + + int totalCount = 0; + for (Map.Entry entry : taskParams.entrySet()) { + Object value = entry.getValue(); + if (value instanceof List) { + totalCount += ((List) value).size(); + } else if (value instanceof String && StringUtils.isNumeric(entry.getKey())) { + try { + List jsonArray = JacksonUtil.parseArray((String) value, Object.class); + if (jsonArray != null) { + totalCount += jsonArray.size(); + } + } catch (Exception e) { + log.warn("解析任务参数失败: key={}, value={}", entry.getKey(), value); + } + } + } + return totalCount; + } + + private boolean hasNewSegments(Map originalParams, Map> currentParams) { + if (currentParams == null || currentParams.isEmpty()) { + return false; + } + + if (originalParams == null || originalParams.isEmpty()) { + return !currentParams.isEmpty(); + } + + for (Map.Entry> entry : currentParams.entrySet()) { + String deviceKey = entry.getKey(); + List currentSources = entry.getValue(); + + if (!originalParams.containsKey(deviceKey)) { + if (currentSources != null && !currentSources.isEmpty()) { + return true; + } + } else { + Object originalValue = originalParams.get(deviceKey); + int originalCount = 0; + + if (originalValue instanceof List) { + originalCount = ((List) originalValue).size(); + } else if (originalValue instanceof String) { + try { + List jsonArray = JacksonUtil.parseArray((String) originalValue, Object.class); + if (jsonArray != null) { + originalCount = jsonArray.size(); + } + } catch (Exception e) { + log.warn("解析原始参数失败: key={}, value={}", deviceKey, originalValue); + } + } + + int currentCount = currentSources != null ? currentSources.size() : 0; + if (currentCount > originalCount) { + return true; + } + } + } + + return false; + } }