diff --git a/src/main/java/com/ycwl/basic/service/pc/impl/FaceServiceImpl.java b/src/main/java/com/ycwl/basic/service/pc/impl/FaceServiceImpl.java index dcbdc3f1..35595f77 100644 --- a/src/main/java/com/ycwl/basic/service/pc/impl/FaceServiceImpl.java +++ b/src/main/java/com/ycwl/basic/service/pc/impl/FaceServiceImpl.java @@ -1133,33 +1133,47 @@ public class FaceServiceImpl implements FaceService { throw new BaseException("人脸识别服务不可用,请稍后再试"); } - // 2. 对每个faceSample进行人脸搜索 - List searchResults = new ArrayList<>(); - for (FaceSampleEntity faceSample : faceSamples) { - try { - SearchFaceRespVo result = faceService.searchFace(faceBodyAdapter, - String.valueOf(face.getScenicId()), - faceSample.getFaceUrl(), - "自定义人脸匹配"); - if (result != null) { - searchResults.add(result); - } - } catch (Exception e) { - log.warn("人脸样本搜索失败,faceSampleId={}, faceUrl={}", - faceSample.getId(), faceSample.getFaceUrl(), e); - // 继续处理其他样本,不中断整个流程 - } - } + // 获取face_select_post_mode配置,默认为0(并集) + Integer faceSelectPostMode = scenicConfig != null ? scenicConfig.getInteger("face_select_post_mode", 0) : 0; + log.debug("face_select_post_mode配置值: {}", faceSelectPostMode); - if (searchResults.isEmpty()) { - log.warn("所有人脸样本搜索都失败,faceId={}, faceSampleIds={}", faceId, faceSampleIds); - throw new BaseException("人脸识别失败,请重试"); - } - - // 3. 整合多个搜索结果 - SearchFaceRespVo mergedResult = mergeSearchResults(searchResults); + SearchFaceRespVo mergedResult; - // 4. 应用后置筛选逻辑 + // 2. 根据face_select_post_mode决定搜索策略 + if (Integer.valueOf(2).equals(faceSelectPostMode)) { + // 模式2:不搜索,直接使用用户选择的faceSampleIds + log.debug("使用模式2:直接使用用户选择的人脸样本,不进行搜索"); + mergedResult = createDirectResult(faceSampleIds); + } else { + // 模式0(并集)和模式1(交集):需要进行搜索 + // 2.1 对每个faceSample进行人脸搜索 + List searchResults = new ArrayList<>(); + for (FaceSampleEntity faceSample : faceSamples) { + try { + SearchFaceRespVo result = faceService.searchFace(faceBodyAdapter, + String.valueOf(face.getScenicId()), + faceSample.getFaceUrl(), + "自定义人脸匹配"); + if (result != null) { + searchResults.add(result); + } + } catch (Exception e) { + log.warn("人脸样本搜索失败,faceSampleId={}, faceUrl={}", + faceSample.getId(), faceSample.getFaceUrl(), e); + // 继续处理其他样本,不中断整个流程 + } + } + + if (searchResults.isEmpty()) { + log.warn("所有人脸样本搜索都失败,faceId={}, faceSampleIds={}", faceId, faceSampleIds); + throw new BaseException("人脸识别失败,请重试"); + } + + // 2.2 根据模式整合多个搜索结果 + mergedResult = mergeSearchResults(searchResults, faceSelectPostMode); + } + + // 3. 应用后置筛选逻辑 if (mergedResult.getSampleListIds() != null && !mergedResult.getSampleListIds().isEmpty()) { List allFaceSampleList = faceSampleMapper.listByIds(mergedResult.getSampleListIds()); List filteredSampleIds = faceService.applySampleFilters(mergedResult.getSampleListIds(), allFaceSampleList, scenicConfig); @@ -1217,22 +1231,33 @@ public class FaceServiceImpl implements FaceService { } /** - * 合并多个搜索结果 + * 合并多个搜索结果(兼容老版本,默认使用并集模式) */ private SearchFaceRespVo mergeSearchResults(List searchResults) { + return mergeSearchResults(searchResults, 0); + } + + /** + * 合并多个搜索结果 + * + * @param searchResults 搜索结果列表 + * @param mergeMode 合并模式:0-并集,1-交集 + * @return 合并后的结果 + */ + private SearchFaceRespVo mergeSearchResults(List searchResults, Integer mergeMode) { SearchFaceRespVo mergedResult = new SearchFaceRespVo(); - // 收集所有样本ID并去重 - Set allSampleIds = new LinkedHashSet<>(); + if (searchResults == null || searchResults.isEmpty()) { + return mergedResult; + } + List allSearchJsons = new ArrayList<>(); float maxScore = 0f; float maxFirstMatchRate = 0f; boolean hasLowThreshold = false; + // 收集基础信息 for (SearchFaceRespVo result : searchResults) { - if (result.getSampleListIds() != null) { - allSampleIds.addAll(result.getSampleListIds()); - } if (result.getSearchResultJson() != null) { allSearchJsons.add(result.getSearchResultJson()); } @@ -1247,17 +1272,92 @@ public class FaceServiceImpl implements FaceService { } } - mergedResult.setSampleListIds(new ArrayList<>(allSampleIds)); + // 根据合并模式处理样本ID + List finalSampleIds; + if (Integer.valueOf(1).equals(mergeMode)) { + // 模式1:交集 - 只保留所有搜索结果中都出现的样本ID + finalSampleIds = computeIntersection(searchResults); + log.debug("使用交集模式合并搜索结果,交集样本数: {}", finalSampleIds.size()); + } else { + // 模式0:并集(默认) - 收集所有样本ID并去重 + Set allSampleIds = new LinkedHashSet<>(); + for (SearchFaceRespVo result : searchResults) { + if (result.getSampleListIds() != null) { + allSampleIds.addAll(result.getSampleListIds()); + } + } + finalSampleIds = new ArrayList<>(allSampleIds); + log.debug("使用并集模式合并搜索结果,并集样本数: {}", finalSampleIds.size()); + } + + mergedResult.setSampleListIds(finalSampleIds); mergedResult.setSearchResultJson(String.join("|", allSearchJsons)); mergedResult.setScore(maxScore); mergedResult.setFirstMatchRate(maxFirstMatchRate); mergedResult.setLowThreshold(hasLowThreshold); - log.debug("合并搜索结果完成,总样本数: {}", allSampleIds.size()); + log.debug("合并搜索结果完成,模式={}, 最终样本数: {}", mergeMode, finalSampleIds.size()); return mergedResult; } + /** + * 计算多个搜索结果的交集 + * 返回在所有搜索结果中都出现的样本ID + */ + private List computeIntersection(List searchResults) { + if (searchResults == null || searchResults.isEmpty()) { + return new ArrayList<>(); + } + + // 过滤掉空结果 + List> validSampleLists = searchResults.stream() + .filter(result -> result.getSampleListIds() != null && !result.getSampleListIds().isEmpty()) + .map(SearchFaceRespVo::getSampleListIds) + .toList(); + + if (validSampleLists.isEmpty()) { + return new ArrayList<>(); + } + + // 如果只有一个有效结果,直接返回 + if (validSampleLists.size() == 1) { + return new ArrayList<>(validSampleLists.getFirst()); + } + + // 计算交集:从第一个列表开始,保留在所有其他列表中都出现的ID + Set intersection = new LinkedHashSet<>(validSampleLists.getFirst()); + + for (int i = 1; i < validSampleLists.size(); i++) { + intersection.retainAll(validSampleLists.get(i)); + } + + return new ArrayList<>(intersection); + } + + /** + * 创建直接结果(模式2:不搜索,直接使用用户选择的faceSampleIds) + * + * @param faceSampleIds 用户选择的人脸样本ID列表 + * @return 搜索结果对象 + */ + private SearchFaceRespVo createDirectResult(List faceSampleIds) { + SearchFaceRespVo result = new SearchFaceRespVo(); + + // 直接使用用户选择的faceSampleIds作为结果 + result.setSampleListIds(new ArrayList<>(faceSampleIds)); + + // 设置默认值 + result.setScore(1.0f); + result.setFirstMatchRate(1.0f); + result.setLowThreshold(false); + result.setSearchResultJson(""); + + log.debug("创建直接结果,样本数: {}", faceSampleIds.size()); + + return result; + } + /** * 记录自定义人脸匹配次数到Redis *