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()) { 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()) { 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(); } // 统计每个 placeholder 在模板中出现的次数 Map placeholderCounts = templatePlaceholders.stream() .collect(Collectors.groupingBy( placeholder -> placeholder, Collectors.counting() )); Map> filteredParams = new HashMap<>(); 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); int actualCount = Math.min(requiredCount.intValue(), allSources.size()); List selectedSources = allSources.subList(0, actualCount); filteredParams.put(imageKey, new ArrayList<>(selectedSources)); } } else { // 视频源:占位符直接对应设备ID String videoKey = placeholder; if (allTaskParams.containsKey(videoKey)) { List allSources = allTaskParams.get(videoKey); int actualCount = Math.min(requiredCount.intValue(), allSources.size()); List selectedSources = allSources.subList(0, actualCount); filteredParams.put(videoKey, new ArrayList<>(selectedSources)); } } } log.debug("filterTaskParams: templateId:{}, original keys:{}, filtered keys:{}, placeholder counts:{}", templateId, allTaskParams.keySet().size(), filteredParams.keySet().size(), placeholderCounts); return filteredParams; } 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; } }