package com.ycwl.basic.biz; import com.ycwl.basic.mapper.SourceMapper; import com.ycwl.basic.model.pc.face.entity.FaceEntity; import com.ycwl.basic.model.pc.face.resp.FaceRespVO; import com.ycwl.basic.model.pc.faceSample.entity.FaceSampleEntity; import com.ycwl.basic.model.pc.faceSample.resp.FaceSampleRespVO; import com.ycwl.basic.model.pc.source.entity.SourceEntity; import com.ycwl.basic.model.pc.template.entity.TemplateConfigEntity; import com.ycwl.basic.repository.FaceRepository; import com.ycwl.basic.repository.SourceRepository; import com.ycwl.basic.repository.TemplateRepository; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @Slf4j @Component public class TemplateBiz { @Autowired private TemplateRepository templateRepository; @Autowired private FaceRepository faceRepository; @Autowired private SourceMapper sourceMapper; public boolean determineTemplateCanGenerate(Long templateId, Long faceId) { return determineTemplateCanGenerate(templateId, faceId, true); } public boolean determineTemplateCanGenerate(Long templateId, Long faceId, boolean scanSource) { List placeholderList = templateRepository.getTemplatePlaceholder(templateId); TemplateConfigEntity templateConfig = templateRepository.getTemplateConfig(templateId); int minimalPlaceholderFill = 1; if (null != templateConfig) { if (null != templateConfig.getMinimalPlaceholderFill()) { minimalPlaceholderFill = templateConfig.getMinimalPlaceholderFill(); } } if (minimalPlaceholderFill <= 0) { return true; } FaceEntity face = faceRepository.getFace(faceId); long count; if (scanSource) { List sourceEntities = sourceMapper.listVideoByScenicFaceRelation(face.getScenicId(), faceId); if (sourceEntities == null || sourceEntities.isEmpty()) { log.info("faceId:{} has no source", faceId); return false; } count = sourceEntities.stream() .map(SourceEntity::getDeviceId) .filter(Objects::nonNull) // 添加对 null 的检查 .distinct() .filter(deviceId -> placeholderList.contains(deviceId.toString())) .count(); } else { List faceSampleList = faceRepository.getFaceSampleList(faceId); if (faceSampleList == null || faceSampleList.isEmpty()) { log.info("faceId:{} has no faceSample", faceId); return false; } count = faceSampleList.stream() .map(FaceSampleEntity::getDeviceId) .filter(Objects::nonNull) // 添加对 null 的检查 .distinct() .filter(deviceId -> placeholderList.contains(deviceId.toString())) .count(); } return count >= minimalPlaceholderFill; } public boolean determineTemplateCanAutoGenerate(Long templateId, Long faceId) { return determineTemplateCanAutoGenerate(templateId, faceId, true); } public boolean determineTemplateCanAutoGenerate(Long templateId, Long faceId, boolean scanSource) { List placeholderList = templateRepository.getTemplatePlaceholder(templateId); TemplateConfigEntity templateConfig = templateRepository.getTemplateConfig(templateId); Integer minimalPlaceholderFill = null; if (null != templateConfig) { if (null != templateConfig.getAutomaticPlaceholderFill()) { minimalPlaceholderFill = templateConfig.getAutomaticPlaceholderFill(); } } if (minimalPlaceholderFill == null) { // 未开启 log.info("模板:{},未配置最小自动生成功能,默认生成!", templateId); minimalPlaceholderFill = 1; } if (minimalPlaceholderFill <= 0) { return true; } FaceEntity face = faceRepository.getFace(faceId); List faceSampleList = faceRepository.getFaceSampleList(faceId); if (faceSampleList.isEmpty()) { return false; } long count; if (scanSource) { List sourceEntities = sourceMapper.listVideoByScenicFaceRelation(face.getScenicId(), faceId); count = sourceEntities.stream() .map(SourceEntity::getDeviceId) .filter(Objects::nonNull) // 添加对 null 的检查 .distinct() .filter(deviceId -> placeholderList.contains(deviceId.toString())) .count(); } else { count = faceSampleList.stream() .map(FaceSampleEntity::getDeviceId) .distinct() .filter(deviceId -> placeholderList.contains(deviceId.toString())) .count(); } return count >= minimalPlaceholderFill; } public Map> filterTaskParams(Long templateId, Map> allTaskParams) { if (allTaskParams == null || allTaskParams.isEmpty()) { return Map.of(); } List templatePlaceholders = templateRepository.getTemplatePlaceholder(templateId); if (templatePlaceholders == null || templatePlaceholders.isEmpty()) { log.info("filterTaskParams: templateId:{} has no placeholders", templateId); return Map.of(); } TemplateConfigEntity templateConfig = templateRepository.getTemplateConfig(templateId); // 统计每个 placeholder 在模板中出现的次数 Map placeholderCounts = templatePlaceholders.stream() .collect(Collectors.groupingBy( placeholder -> placeholder, Collectors.counting() )); Map> filteredParams = new HashMap<>(); // 判断是否允许片段重复 boolean allowDuplicate = templateConfig != null && Integer.valueOf(1).equals(templateConfig.getDuplicateEnable()); for (Map.Entry entry : placeholderCounts.entrySet()) { String placeholder = entry.getKey(); Long requiredCount = entry.getValue(); if (placeholder.startsWith("P")) { // 图片源:占位符格式为 "P{deviceId}" String imageKey = placeholder; if (allTaskParams.containsKey(imageKey)) { List allSources = allTaskParams.get(imageKey); List selectedSources = selectSources(allSources, requiredCount.intValue(), allowDuplicate); if (!selectedSources.isEmpty()) { filteredParams.put(imageKey, selectedSources); } } } else { // 视频源:占位符直接对应设备ID String videoKey = placeholder; if (allTaskParams.containsKey(videoKey)) { List allSources = allTaskParams.get(videoKey); List selectedSources = selectSources(allSources, requiredCount.intValue(), allowDuplicate); if (!selectedSources.isEmpty()) { filteredParams.put(videoKey, selectedSources); } } } } log.debug("filterTaskParams: templateId:{}, original keys:{}, filtered keys:{}, placeholder counts:{}, allowDuplicate:{}", templateId, allTaskParams.keySet().size(), filteredParams.keySet().size(), placeholderCounts, allowDuplicate); return filteredParams; } private List selectSources(List allSources, int requiredCount, boolean allowDuplicate) { if (allSources == null || allSources.isEmpty()) { return new ArrayList<>(); } if (!allowDuplicate) { // 不允许重复,使用原有逻辑 int actualCount = Math.min(requiredCount, allSources.size()); return new ArrayList<>(allSources.subList(0, actualCount)); } // 允许重复,循环填充到所需数量 List selectedSources = new ArrayList<>(); int sourceIndex = 0; for (int i = 0; i < requiredCount; i++) { selectedSources.add(allSources.get(sourceIndex)); sourceIndex = (sourceIndex + 1) % allSources.size(); } return selectedSources; } public Long findFirstAvailableTemplate(List templateIds, Long faceId, boolean scanSource) { if (templateIds == null || templateIds.isEmpty() || faceId == null) { return null; } for (Long templateId : templateIds) { if (determineTemplateCanGenerate(templateId, faceId, scanSource)) { return templateId; } } return null; } }